Browse Source

Merge branch 'dev' into studio-dev

pull/10363/head
Yunus Emre Kalkan 5 years ago
parent
commit
d057e90cc6
  1. 2
      .github/workflows/build-and-test.yml
  2. 2
      Directory.Build.props
  3. 5
      common.props
  4. 29
      docs/en/Best-Practices/Entity-Framework-Core-Integration.md
  5. 26
      docs/en/Best-Practices/MongoDB-Integration.md
  6. 9
      docs/en/Road-Map.md
  7. 67
      docs/en/UI/Angular/How-Replaceable-Components-Work-with-Extensions.md
  8. 276
      docs/en/UI/Angular/Permission-Management-Component-Replacement.md
  9. 7
      framework/Volo.Abp.sln
  10. 2
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj
  11. 2
      framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj
  12. 2
      framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj
  13. 2
      framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Volo.Abp.AspNetCore.Components.Server.Theming.csproj
  14. 2
      framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj
  15. 2
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Volo.Abp.AspNetCore.Components.Web.Theming.csproj
  16. 2
      framework/src/Volo.Abp.AspNetCore.Components.Web/Volo.Abp.AspNetCore.Components.Web.csproj
  17. 2
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.csproj
  18. 2
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj
  19. 2
      framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj
  20. 2
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.csproj
  21. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj
  22. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs
  23. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj
  24. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions.csproj
  25. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj
  26. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj
  27. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo.Abp.AspNetCore.Mvc.UI.Packages.csproj
  28. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj
  29. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj
  30. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj
  31. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj
  32. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  33. 25
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  34. 13
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
  35. 24
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  36. 24
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
  37. 4
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs
  38. 2
      framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj
  39. 2
      framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj
  40. 2
      framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj
  41. 6
      framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestProxyHttpClientFactory.cs
  42. 17
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Http/AbpHttpRequestExtensions.cs
  43. 2
      framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
  44. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpAuthorizationExceptionHandlerOptions.cs
  45. 45
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
  46. 67
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultAbpAuthorizationExceptionHandler.cs
  47. 11
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IAbpAuthorizationExceptionHandler.cs
  48. 3
      framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xml
  49. 30
      framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xsd
  50. 21
      framework/src/Volo.Abp.Auditing.Contracts/Volo.Abp.Auditing.Contracts.csproj
  51. 9
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AbpAuditingContractsModule.cs
  52. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AuditedAttribute.cs
  53. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/DisableAuditingAttribute.cs
  54. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/EntityChangeType.cs
  55. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditedObject.cs
  56. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditingEnabled.cs
  57. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/ICreationAuditedObject.cs
  58. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IDeletionAuditedObject.cs
  59. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IFullAuditedObject.cs
  60. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasCreationTime.cs
  61. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasDeletionTime.cs
  62. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasModificationTime.cs
  63. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMayHaveCreator.cs
  64. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IModificationAuditedObject.cs
  65. 0
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMustHaveCreator.cs
  66. 1
      framework/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj
  67. 3
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
  68. 2
      framework/src/Volo.Abp.Autofac.WebAssembly/Volo.Abp.Autofac.WebAssembly.csproj
  69. 2
      framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj
  70. 2
      framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj
  71. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs
  72. 86
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs
  73. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs
  74. 29
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs
  75. 165
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProxyCommandBase.cs
  76. 31
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/RemoveProxyCommand.cs
  77. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/AbpCliServiceProxyOptions.cs
  78. 115
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs
  79. 400
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
  80. 55
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/GenerateProxyArgs.cs
  81. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/IServiceProxyGenerator.cs
  82. 90
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/JavaScript/JavaScriptServiceProxyGenerator.cs
  83. 56
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/ServiceProxyGeneratorBase.cs
  84. 2
      framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj
  85. 3
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  86. 2
      framework/src/Volo.Abp.Core/Volo/Abp/ISoftDelete.cs
  87. 2
      framework/src/Volo.Abp.Dapper/Volo.Abp.Dapper.csproj
  88. 4
      framework/src/Volo.Abp.Data/Volo/Abp/Data/DataFilter.cs
  89. 2
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo.Abp.Ddd.Application.Contracts.csproj
  90. 4
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/AbpDddApplicationContractsModule.cs
  91. 1
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleAuditedEntityDto.cs
  92. 1
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleAuditedEntityWithUserDto.cs
  93. 1
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleCreationAuditedEntityDto.cs
  94. 1
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleCreationAuditedEntityWithUserDto.cs
  95. 1
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleFullAuditedEntityDto.cs
  96. 1
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleFullAuditedEntityWithUserDto.cs
  97. 2
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj
  98. 2
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj
  99. 2
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj
  100. 2
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj

2
.github/workflows/build-and-test.yml

@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@master
with:
dotnet-version: 5.0.201
dotnet-version: 6.0.100-rc.1.21458.32
- name: Build All
run: .\build-all.ps1

2
Directory.Build.props

@ -2,7 +2,7 @@
<PropertyGroup>
<!-- All Microsoft packages -->
<MicrosoftPackageVersion>5.0.*</MicrosoftPackageVersion>
<MicrosoftPackageVersion>6.0.0-rc.*</MicrosoftPackageVersion>
<!-- Microsoft.NET.Test.Sdk https://www.nuget.org/packages/Microsoft.NET.Test.Sdk -->
<MicrosoftNETTestSdkPackageVersion>16.9.1</MicrosoftNETTestSdkPackageVersion>

5
common.props

@ -28,4 +28,9 @@
</None>
</ItemGroup>
<ItemGroup Condition="$(AssemblyName.EndsWith('HttpApi.Client'))">
<EmbeddedResource Include="**\*generate-proxy.json" />
<Content Remove="**\*generate-proxy.json" />
</ItemGroup>
</Project>

29
docs/en/Best-Practices/Entity-Framework-Core-Integration.md

@ -66,12 +66,7 @@ public static string Schema { get; set; } = AbpIdentityConsts.DefaultDbSchema;
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureIdentity(options =>
{
options.TablePrefix = TablePrefix;
options.Schema = Schema;
});
builder.ConfigureIdentity();
}
````
@ -80,25 +75,20 @@ protected override void OnModelCreating(ModelBuilder builder)
````C#
public static class IdentityDbContextModelBuilderExtensions
{
public static void ConfigureIdentity(
[NotNull] this ModelBuilder builder,
Action<IdentityModelBuilderConfigurationOptions> optionsAction = null)
public static void ConfigureIdentity([NotNull] this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
var options = new IdentityModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<IdentityUser>(b =>
{
b.ToTable(options.TablePrefix + "Users", options.Schema);
b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users", AbpIdentityDbProperties.DbSchema);
b.ConfigureByConvention();
//code omitted for brevity
});
builder.Entity<IdentityUserClaim>(b =>
{
b.ToTable(options.TablePrefix + "UserClaims", options.Schema);
b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "UserClaims", AbpIdentityDbProperties.DbSchema);
b.ConfigureByConvention();
//code omitted for brevity
});
@ -109,17 +99,6 @@ public static class IdentityDbContextModelBuilderExtensions
````
* **Do** call `b.ConfigureByConvention();` for each entity mapping (as shown above).
* **Do** create a **configuration options** class by inheriting from the `AbpModelBuilderConfigurationOptions`. Example:
````C#
public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{
public IdentityModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema)
{
}
}
````
### Repository Implementation

26
docs/en/Best-Practices/MongoDB-Integration.md

@ -55,10 +55,7 @@ protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
modelBuilder.ConfigureIdentity(options =>
{
options.CollectionPrefix = CollectionPrefix;
});
modelBuilder.ConfigureIdentity();
}
```
@ -73,36 +70,19 @@ public static class AbpIdentityMongoDbContextExtensions
{
Check.NotNull(builder, nameof(builder));
var options = new IdentityMongoModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<IdentityUser>(b =>
{
b.CollectionName = options.CollectionPrefix + "Users";
b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Users";
});
builder.Entity<IdentityRole>(b =>
{
b.CollectionName = options.CollectionPrefix + "Roles";
b.CollectionName = AbpIdentityDbProperties.DbTablePrefix + "Roles";
});
}
}
```
- **Do** create a **configuration options** class by inheriting from the `AbpMongoModelBuilderConfigurationOptions`. Example:
```c#
public class IdentityMongoModelBuilderConfigurationOptions
: AbpMongoModelBuilderConfigurationOptions
{
public IdentityMongoModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix)
{
}
}
```
### Repository Implementation
- **Do** **inherit** the repository from the `MongoDbRepository<TMongoDbContext, TEntity, TKey>` class and implement the corresponding repository interface. Example:

9
docs/en/Road-Map.md

@ -10,12 +10,9 @@ This version will focus on the following works:
* Upgrading to .NET 6.0 ([#9004](https://github.com/abpframework/abp/issues/9004))
* Upgrading to Bootstrap 5.x ([#8922](https://github.com/abpframework/abp/issues/8922))
* Alternative to IdentityServer4 ([#7221](https://github.com/abpframework/abp/issues/7221))
* C# and JavaScript Static Client Proxy Generation ([#9864](https://github.com/abpframework/abp/issues/9864))
* Revisit the microservice demo solution ([#8385](https://github.com/abpframework/abp/issues/8385))
* Dapr integration ([#2183](https://github.com/abpframework/abp/issues/2183))
* Publishing distributed events as transactional ([#6126](https://github.com/abpframework/abp/issues/6126))
* Resource based authorization system ([#236](https://github.com/abpframework/abp/issues/236))
* API Versioning system: finalize & document ([#497](https://github.com/abpframework/abp/issues/497))
* Performance optimizations; Enabling .NET Trimming, using source generators and reducing reflection, etc.
* Improving the abp.io platform and work more on contents and documents
@ -27,6 +24,10 @@ The *Next Versions* section above shows the main focus of the planned versions.
Here, a list of major items in the backlog we are considering to work on in the next versions.
* ([#497](https://github.com/abpframework/abp/issues/497)) API Versioning system: finalize & document
* ([#7221](https://github.com/abpframework/abp/issues/7221)) Alternative to IdentityServer4
* ([#2183](https://github.com/abpframework/abp/issues/2183)) Dapr integration
* ([#236](https://github.com/abpframework/abp/issues/236)) Resource based authorization system
* [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a gRPC integration infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](https://docs.abp.io/en/abp/latest/Modules/Index))
* [#1754](https://github.com/abpframework/abp/issues/1754) / Multi-lingual entities
* [#57](https://github.com/abpframework/abp/issues/57) / Built-in CQRS infrastructure

67
docs/en/UI/Angular/How-Replaceable-Components-Work-with-Extensions.md

@ -17,30 +17,18 @@ yarn ng generate component my-roles/my-roles --flat --export
Open the generated `src/app/my-roles/my-roles.component.ts` file and replace its content with the following:
```js
import { ListService, PagedAndSortedResultRequestDto } from '@abp/ng.core';
import {
CreateRole,
DeleteRole,
eIdentityComponents,
GetRoleById,
GetRoles,
IdentityRoleDto,
IdentityState,
RolesComponent,
UpdateRole,
} from '@abp/ng.identity';
import { ListService, PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core';
import { eIdentityComponents, IdentityRoleDto, IdentityRoleService, RolesComponent } from '@abp/ng.identity';
import { ePermissionManagementComponents } from '@abp/ng.permission-management';
import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared';
import {
EXTENSIONS_IDENTIFIER,
FormPropData,
generateFormFromProps,
generateFormFromProps
} from '@abp/ng.theme.shared/extensions';
import { Component, Injector, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { finalize, pluck } from 'rxjs/operators';
import { finalize } from 'rxjs/operators';
@Component({
selector: 'app-my-roles',
@ -51,15 +39,14 @@ import { finalize, pluck } from 'rxjs/operators';
provide: EXTENSIONS_IDENTIFIER,
useValue: eIdentityComponents.Roles,
},
{ provide: RolesComponent, useExisting: MyRolesComponent },
{
provide: RolesComponent,
useExisting: MyRolesComponent
}
],
})
export class MyRolesComponent implements OnInit {
@Select(IdentityState.getRoles)
data$: Observable<IdentityRoleDto[]>;
@Select(IdentityState.getRolesTotalCount)
totalCount$: Observable<number>;
data: PagedResultDto<IdentityRoleDto> = { items: [], totalCount: 0 };
form: FormGroup;
@ -82,8 +69,8 @@ export class MyRolesComponent implements OnInit {
constructor(
public readonly list: ListService<PagedAndSortedResultRequestDto>,
protected confirmationService: ConfirmationService,
protected store: Store,
protected injector: Injector
protected injector: Injector,
protected service: IdentityRoleService,
) {}
ngOnInit() {
@ -106,25 +93,21 @@ export class MyRolesComponent implements OnInit {
}
edit(id: string) {
this.store
.dispatch(new GetRoleById(id))
.pipe(pluck('IdentityState', 'selectedRole'))
.subscribe(selectedRole => {
this.selected = selectedRole;
this.openModal();
});
this.service.get(id).subscribe(res => {
this.selected = res;
this.openModal();
});
}
save() {
if (!this.form.valid) return;
this.modalBusy = true;
this.store
.dispatch(
this.selected.id
? new UpdateRole({ ...this.selected, ...this.form.value, id: this.selected.id })
: new CreateRole(this.form.value)
)
const { id } = this.selected;
(id
? this.service.update(id, { ...this.selected, ...this.form.value })
: this.service.create(this.form.value)
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.isModalVisible = false;
@ -139,13 +122,13 @@ export class MyRolesComponent implements OnInit {
})
.subscribe((status: Confirmation.Status) => {
if (status === Confirmation.Status.confirm) {
this.store.dispatch(new DeleteRole(id)).subscribe(() => this.list.get());
this.service.delete(id).subscribe(() => this.list.get());
}
});
}
private hookToQuery() {
this.list.hookToQuery(query => this.store.dispatch(new GetRoles(query))).subscribe();
this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res));
}
openPermissionsModal(providerKey: string) {
@ -189,15 +172,15 @@ Open the generated `src/app/my-role/my-role.component.html` file and replace its
<h5 class="card-title">My Roles</h5>
</div>
<div class="text-right col col-md-6">
<abp-page-toolbar [record]="data$ | async"></abp-page-toolbar>
<abp-page-toolbar [record]="data.items"></abp-page-toolbar>
</div>
</div>
</div>
<div class="card-body">
<abp-extensible-table
[data]="data$ | async"
[recordsTotal]="totalCount$ | async"
[data]="data.items"
[recordsTotal]="data.totalCount"
[list]="list"
></abp-extensible-table>
</div>

276
docs/en/UI/Angular/Permission-Management-Component-Replacement.md

@ -5,37 +5,27 @@
Run the following command in `angular` folder to create a new component called `PermissionManagementComponent`.
```bash
yarn ng generate component permission-management --entryComponent --inlineStyle
# You don't need the --entryComponent option in Angular 9
yarn ng generate component permission-management --inlineStyle
```
Open the generated `permission-management.component.ts` in `src/app/permission-management` folder and replace the content with the following:
```js
import { ConfigStateService, CurrentUserDto, ReplaceableComponents } from '@abp/ng.core';
import {
GetPermissionListResultDto,
PermissionGrantInfoDto, PermissionGroupDto, PermissionManagement, PermissionsService, ProviderInfoDto, UpdatePermissionDto
} from '@abp/ng.permission-management';
import { LocaleDirection } from '@abp/ng.theme.shared';
import {
Component,
EventEmitter,
Input,
Output,
Renderer2,
TrackByFunction,
Inject,
Optional,
EventEmitter, Inject, Input, Optional, Output, TrackByFunction
} from '@angular/core';
import { ReplaceableComponents } from '@abp/ng.core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { finalize, map, pluck, take, tap } from 'rxjs/operators';
import {
GetPermissions,
UpdatePermissions,
PermissionManagement,
PermissionManagementState,
} from '@abp/ng.permission-management';
import { of } from 'rxjs';
import { finalize, switchMap, tap } from 'rxjs/operators';
type PermissionWithMargin = PermissionManagement.Permission & {
margin: number;
type PermissionWithStyle = PermissionGrantInfoDto & {
style: string;
};
@Component({
@ -52,8 +42,8 @@ type PermissionWithMargin = PermissionManagement.Permission & {
})
export class PermissionManagementComponent
implements
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
protected _providerName: string;
@Input()
get providerName(): string {
@ -115,15 +105,11 @@ export class PermissionManagementComponent
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(PermissionManagementState.getPermissionGroups)
groups$: Observable<PermissionManagement.Group[]>;
data: GetPermissionListResultDto = { groups: [], entityDisplayName: null };
@Select(PermissionManagementState.getEntityDisplayName)
entityName$: Observable<string>;
selectedGroup: PermissionGroupDto;
selectedGroup: PermissionManagement.Group;
permissions: PermissionManagement.Permission[] = [];
permissions: PermissionGrantInfoDto[] = [];
selectThisTab = false;
@ -131,25 +117,26 @@ export class PermissionManagementComponent
modalBusy = false;
trackByFn: TrackByFunction<PermissionManagement.Group> = (_, item) => item.name;
get selectedGroupPermissions$(): Observable<PermissionWithMargin[]> {
return this.groups$.pipe(
map((groups) =>
this.selectedGroup
? groups.find((group) => group.name === this.selectedGroup.name).permissions
: []
),
map<PermissionManagement.Permission[], PermissionWithMargin[]>((permissions) =>
permissions.map(
(permission) =>
(({
...permission,
margin: findMargin(permissions, permission),
isGranted: this.permissions.find((per) => per.name === permission.name).isGranted,
} as any) as PermissionWithMargin)
)
)
trackByFn: TrackByFunction<PermissionGroupDto> = (_, item) => item.name;
get selectedGroupPermissions(): PermissionWithStyle[] {
if (!this.selectedGroup) return [];
const margin = `margin-${
(document.body.dir as LocaleDirection) === 'rtl' ? 'right' : 'left'
}.px`;
const permissions = this.data.groups.find(
group => group.name === this.selectedGroup.name,
).permissions;
return permissions.map(
permission =>
({
...permission,
style: { [margin]: findMargin(permissions, permission) },
isGranted: this.permissions.find(per => per.name === permission.name).isGranted,
} as unknown as PermissionWithStyle),
);
}
@ -166,21 +153,22 @@ export class PermissionManagementComponent
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs
>,
private store: Store
private service: PermissionsService,
private configState: ConfigStateService
) {}
getChecked(name: string) {
return (this.permissions.find((per) => per.name === name) || { isGranted: false }).isGranted;
return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted;
}
isGrantedByOtherProviderName(grantedProviders: PermissionManagement.GrantedProvider[]): boolean {
isGrantedByOtherProviderName(grantedProviders: ProviderInfoDto[]): boolean {
if (grantedProviders.length) {
return grantedProviders.findIndex((p) => p.providerName !== this.providerName) > -1;
return grantedProviders.findIndex(p => p.providerName !== this.providerName) > -1;
}
return false;
}
onClickCheckbox(clickedPermission: PermissionManagement.Permission, value) {
onClickCheckbox(clickedPermission: PermissionGrantInfoDto, value) {
if (
clickedPermission.isGranted &&
this.isGrantedByOtherProviderName(clickedPermission.grantedProviders)
@ -188,7 +176,7 @@ export class PermissionManagementComponent
return;
setTimeout(() => {
this.permissions = this.permissions.map((per) => {
this.permissions = this.permissions.map(per => {
if (clickedPermission.name === per.name) {
return { ...per, isGranted: !per.isGranted };
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) {
@ -206,24 +194,22 @@ export class PermissionManagementComponent
}
setTabCheckboxState() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
const selectedPermissions = permissions.filter((per) => per.isGranted);
const element = document.querySelector('#select-all-in-this-tabs') as any;
if (selectedPermissions.length === permissions.length) {
element.indeterminate = false;
this.selectThisTab = true;
} else if (selectedPermissions.length === 0) {
element.indeterminate = false;
this.selectThisTab = false;
} else {
element.indeterminate = true;
}
});
const selectedPermissions = this.selectedGroupPermissions.filter(per => per.isGranted);
const element = document.querySelector('#select-all-in-this-tabs') as any;
if (selectedPermissions.length === this.selectedGroupPermissions.length) {
element.indeterminate = false;
this.selectThisTab = true;
} else if (selectedPermissions.length === 0) {
element.indeterminate = false;
this.selectThisTab = false;
} else {
element.indeterminate = true;
}
}
setGrantCheckboxState() {
const selectedAllPermissions = this.permissions.filter((per) => per.isGranted);
const selectedAllPermissions = this.permissions.filter(per => per.isGranted);
const checkboxElement = document.querySelector('#select-all-in-all-tabs') as any;
if (selectedAllPermissions.length === this.permissions.length) {
@ -238,26 +224,24 @@ export class PermissionManagementComponent
}
onClickSelectThisTab() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
permissions.forEach((permission) => {
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders))
return;
const index = this.permissions.findIndex((per) => per.name === permission.name);
this.permissions = [
...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab },
...this.permissions.slice(index + 1),
];
});
this.selectedGroupPermissions.forEach(permission => {
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders))
return;
const index = this.permissions.findIndex(per => per.name === permission.name);
this.permissions = [
...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab },
...this.permissions.slice(index + 1),
];
});
this.setGrantCheckboxState();
}
onClickSelectAll() {
this.permissions = this.permissions.map((permission) => ({
this.permissions = this.permissions.map(permission => ({
...permission,
isGranted:
this.isGrantedByOtherProviderName(permission.grantedProviders) || !this.selectAllTab,
@ -266,43 +250,41 @@ export class PermissionManagementComponent
this.selectThisTab = !this.selectAllTab;
}
onChangeGroup(group: PermissionManagement.Group) {
onChangeGroup(group: PermissionGroupDto) {
this.selectedGroup = group;
this.setTabCheckboxState();
}
submit() {
this.modalBusy = true;
const unchangedPermissions = getPermissions(
this.store.selectSnapshot(PermissionManagementState.getPermissionGroups)
);
const unchangedPermissions = getPermissions(this.data.groups);
const changedPermissions: PermissionManagement.MinimumPermission[] = this.permissions
.filter((per) =>
unchangedPermissions.find((unchanged) => unchanged.name === per.name).isGranted ===
const changedPermissions: UpdatePermissionDto[] = this.permissions
.filter(per =>
unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted ===
per.isGranted
? false
: true
: true,
)
.map(({ name, isGranted }) => ({ name, isGranted }));
if (changedPermissions.length) {
this.store
.dispatch(
new UpdatePermissions({
providerKey: this.providerKey,
providerName: this.providerName,
permissions: changedPermissions,
})
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.visible = false;
});
} else {
this.modalBusy = false;
if (!changedPermissions.length) {
this.visible = false;
return;
}
this.modalBusy = true;
this.service
.update(this.providerName, this.providerKey, { permissions: changedPermissions })
.pipe(
switchMap(() =>
this.shouldFetchAppConfig() ? this.configState.refreshAppState() : of(null),
),
finalize(() => (this.modalBusy = false)),
)
.subscribe(() => {
this.visible = false;
});
}
openModal() {
@ -310,20 +292,13 @@ export class PermissionManagementComponent
throw new Error('Provider Key and Provider Name are required.');
}
return this.store
.dispatch(
new GetPermissions({
providerKey: this.providerKey,
providerName: this.providerName,
})
)
.pipe(
pluck('PermissionManagementState', 'permissionRes'),
tap((permissionRes: PermissionManagement.Response) => {
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
})
);
return this.service.get(this.providerName, this.providerKey).pipe(
tap((permissionRes: GetPermissionListResultDto) => {
this.data = permissionRes;
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
}),
);
}
initModal() {
@ -331,6 +306,13 @@ export class PermissionManagementComponent
this.setGrantCheckboxState();
}
getAssignedCount(groupName: string) {
return this.permissions.reduce(
(acc, val) => (val.name.split('.')[0] === groupName && val.isGranted ? acc + 1 : acc),
0,
);
}
onVisibleChange(visible: boolean) {
this.visible = visible;
@ -339,13 +321,20 @@ export class PermissionManagementComponent
this.replaceableData.outputs.visibleChange(visible);
}
}
shouldFetchAppConfig() {
const currentUser = this.configState.getOne('currentUser') as CurrentUserDto;
if (this.providerName === 'R') return currentUser.roles.some(role => role === this.providerKey);
if (this.providerName === 'U') return currentUser.id === this.providerKey;
return false;
}
}
function findMargin(
permissions: PermissionManagement.Permission[],
permission: PermissionManagement.Permission
) {
const parentPermission = permissions.find((per) => per.name === permission.parentName);
function findMargin(permissions: PermissionGrantInfoDto[], permission: PermissionGrantInfoDto) {
const parentPermission = permissions.find(per => per.name === permission.parentName);
if (parentPermission && parentPermission.parentName) {
let margin = 20;
@ -355,7 +344,7 @@ function findMargin(
return parentPermission ? 20 : 0;
}
function getPermissions(groups: PermissionManagement.Group[]): PermissionManagement.Permission[] {
function getPermissions(groups: PermissionGroupDto[]): PermissionGrantInfoDto[] {
return groups.reduce((acc, val) => [...acc, ...val.permissions], []);
}
```
@ -363,16 +352,12 @@ function getPermissions(groups: PermissionManagement.Group[]): PermissionManagem
Open the generated `permission-management.component.html` in `src/app/permission-management` folder and replace the content with the below:
```html
<abp-modal
[visible]="isVisible"
(visibleChange)="onVisibleChange($event)"
(init)="initModal()"
[busy]="modalBusy"
>
<ng-container *ngIf="{ entityName: entityName$ | async } as data">
<abp-modal [visible]="isVisible" (visibleChange)="onVisibleChange($event)" [busy]="modalBusy">
<ng-container *ngIf="data.entityDisplayName">
<ng-template #abpHeader>
<h4>
{%{{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}}%} - {%{{{ data.entityName }}}%}
{%{{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}}%} -
{%{{{ data.entityDisplayName }}}%}
</h4>
</ng-template>
<ng-template #abpBody>
@ -394,13 +379,18 @@ Open the generated `permission-management.component.html` in `src/app/permission
<div class="row">
<div class="overflow-scroll col-md-4">
<ul class="nav nav-pills flex-column">
<li *ngFor="let group of groups$ | async; trackBy: trackByFn" class="nav-item">
<li *ngFor="let group of data.groups; trackBy: trackByFn" class="nav-item">
<a
*ngIf="{ assignedCount: getAssignedCount(group.name) } as count"
class="nav-link pointer"
[class.active]="selectedGroup?.name === group?.name"
(click)="onChangeGroup(group)"
>{%{{{ group?.displayName }}}%}</a
>
<div [class.font-weight-bold]="count.assignedCount">
{%{{{ group?.displayName }}}%}
<span>({%{{{ count.assignedCount }}}%})</span>
</div>
</a>
</li>
</ul>
</div>
@ -423,12 +413,8 @@ Open the generated `permission-management.component.html` in `src/app/permission
</div>
<hr class="mb-3" />
<div
*ngFor="
let permission of selectedGroupPermissions$ | async;
let i = index;
trackBy: trackByFn
"
[style.margin-left]="permission.margin + 'px'"
*ngFor="let permission of selectedGroupPermissions; let i = index; trackBy: trackByFn"
[ngStyle]="permission.style"
class="custom-checkbox custom-control mb-2"
>
<input

7
framework/Volo.Abp.sln

@ -385,6 +385,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MongoDB.Tests.Seco
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Threading.Tests", "test\Volo.Abp.Threading.Tests\Volo.Abp.Threading.Tests.csproj", "{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Auditing.Contracts", "src\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj", "{508B6355-AD28-4E60-8549-266D21DBF2CF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1147,6 +1149,10 @@ Global
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B}.Release|Any CPU.Build.0 = Release|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{508B6355-AD28-4E60-8549-266D21DBF2CF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1341,6 +1347,7 @@ Global
{75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{90B1866A-EF99-40B9-970E-B898E5AA523F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{7B2FCAD6-86E6-49C8-ADBE-A61B4F4B101B} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{508B6355-AD28-4E60-8549-266D21DBF2CF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

2
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Authentication.JwtBearer</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Authentication.JwtBearer</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Authentication.OAuth</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Authentication.OAuth</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo.Abp.AspNetCore.Authentication.OpenIdConnect.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Volo.Abp.AspNetCore.Components.Server.Theming.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>

2
framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>

2
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Volo.Abp.AspNetCore.Components.Web.Theming.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.Web/Volo.Abp.AspNetCore.Components.Web.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

2
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo.Abp.AspNetCore.Components.WebAssembly.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Components.WebAssembly</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Components.WebAssembly</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Components</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Components</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.MultiTenancy</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.MultiTenancy</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.Client</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.Client</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

1
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs

@ -1,7 +1,6 @@
using System;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
using Volo.Abp.Timing;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
{

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Bootstrap</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Bootstrap</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Bundling</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Bundling</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo.Abp.AspNetCore.Mvc.UI.Packages.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Packages</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Packages</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Widgets</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Widgets</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI</PackageId>

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>Volo.Abp.AspNetCore.Mvc</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc</PackageId>

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

@ -86,6 +86,7 @@ namespace Volo.Abp.AspNetCore.Mvc
var controllerModel = moduleModel.GetOrAddController(
_options.ControllerNameGenerator(controllerType, setting),
FindGroupName(controllerType) ?? apiDescription.GroupName,
controllerType,
_modelOptions.IgnoredInterfaces
);
@ -113,6 +114,14 @@ namespace Volo.Abp.AspNetCore.Mvc
allowAnonymous = false;
}
var implementFrom = controllerType.FullName;
var interfaceType = controllerType.GetInterfaces().FirstOrDefault(i => i.GetMethods().Any(x => x.ToString() == method.ToString()));
if (interfaceType != null)
{
implementFrom = TypeHelper.GetFullNameHandlingNullableAndGenerics(interfaceType);
}
var actionModel = controllerModel.AddAction(
uniqueMethodName,
ActionApiDescriptionModel.Create(
@ -121,7 +130,8 @@ namespace Volo.Abp.AspNetCore.Mvc
apiDescription.RelativePath,
apiDescription.HttpMethod,
GetSupportedVersions(controllerType, method, setting),
allowAnonymous
allowAnonymous,
implementFrom
)
);
@ -351,6 +361,19 @@ namespace Volo.Abp.AspNetCore.Mvc
return ModuleApiDescriptionModel.DefaultRemoteServiceName;
}
private string FindGroupName(Type controllerType)
{
var controllerNameAttribute =
controllerType.GetCustomAttributes().OfType<ControllerNameAttribute>().FirstOrDefault();
if (controllerNameAttribute?.Name != null)
{
return controllerNameAttribute.Name;
}
return null;
}
[CanBeNull]
private ConventionalControllerSetting FindSetting(Type controllerType)
{

13
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs

@ -45,7 +45,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
{
RemoveDuplicateControllers(application);
foreach (var controller in application.Controllers)
foreach (var controller in GetControllers(application))
{
var controllerType = controller.ControllerType.AsType();
@ -71,11 +71,16 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
}
}
protected virtual IList<ControllerModel> GetControllers(ApplicationModel application)
{
return application.Controllers;
}
protected virtual void RemoveDuplicateControllers(ApplicationModel application)
{
var controllerModelsToRemove = new List<ControllerModel>();
foreach (var controllerModel in application.Controllers)
foreach (var controllerModel in GetControllers(application))
{
if (!controllerModel.ControllerType.IsDefined(typeof(ExposeServicesAttribute), false))
{
@ -90,7 +95,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr.IncludeSelf)
{
var exposedControllerModels = application.Controllers
var exposedControllerModels = GetControllers(application)
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType))
.ToArray();
@ -109,7 +114,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
continue;
}
var baseControllerModels = application.Controllers
var baseControllerModels = GetControllers(application)
.Where(cm => baseControllerTypes.Contains(cm.ControllerType))
.ToArray();

24
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

@ -5,10 +5,12 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http;
@ -55,17 +57,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
//TODO: Trigger an AbpExceptionHandled event or something like that.
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>();
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
var logLevel = context.Exception.GetLogLevel();
var remoteServiceErrorInfoBuilder = new StringBuilder();
@ -80,6 +75,21 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
await context.GetRequiredService<IExceptionNotifier>().NotifyAsync(new ExceptionNotificationContext(context.Exception));
if (context.Exception is AbpAuthorizationException)
{
await context.HttpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionHandler>()
.HandleAsync(context.Exception.As<AbpAuthorizationException>(), context.HttpContext);
}
else
{
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
}
context.Exception = null; //Handled!
}
}

24
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs

@ -5,10 +5,12 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http;
@ -67,17 +69,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
//TODO: Trigger an AbpExceptionHandled event or something like that.
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>();
var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, exceptionHandlingOptions.SendExceptionsDetailsToClients);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
var logLevel = context.Exception.GetLogLevel();
var remoteServiceErrorInfoBuilder = new StringBuilder();
@ -91,6 +86,21 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
await context.GetRequiredService<IExceptionNotifier>().NotifyAsync(new ExceptionNotificationContext(context.Exception));
if (context.Exception is AbpAuthorizationException)
{
await context.HttpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionHandler>()
.HandleAsync(context.Exception.As<AbpAuthorizationException>(), context.HttpContext);
}
else
{
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
context.HttpContext.Response.StatusCode = (int) context
.GetRequiredService<IHttpExceptionStatusCodeFinder>()
.GetStatusCode(context.HttpContext, context.Exception);
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo));
}
context.Exception = null; //Handled!
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpJsonOptionsSetup.cs

@ -30,6 +30,10 @@ namespace Volo.Abp.AspNetCore.Mvc.Json
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
options.JsonSerializerOptions.Converters.Add(new AbpHasExtraPropertiesJsonConverterFactory());
// Remove after this PR.
// https://github.com/dotnet/runtime/pull/57525
options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Serilog</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Serilog</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.SignalR</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.SignalR</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.TestBase</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.TestBase</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

6
framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestDynamicProxyHttpClientFactory.cs → framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestProxyHttpClientFactory.cs

@ -1,15 +1,17 @@
using System.Net.Http;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.DynamicProxying;
using Volo.Abp.Http.Client.Proxying;
namespace Volo.Abp.AspNetCore.TestBase.DynamicProxying
{
[Dependency(ReplaceServices = true)]
public class AspNetCoreTestDynamicProxyHttpClientFactory : IDynamicProxyHttpClientFactory, ITransientDependency
public class AspNetCoreTestProxyHttpClientFactory : IProxyHttpClientFactory, ITransientDependency
{
private readonly ITestServerAccessor _testServerAccessor;
public AspNetCoreTestDynamicProxyHttpClientFactory(
public AspNetCoreTestProxyHttpClientFactory(
ITestServerAccessor testServerAccessor)
{
_testServerAccessor = testServerAccessor;

17
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Http/AbpHttpRequestExtensions.cs

@ -1,23 +1,18 @@
using JetBrains.Annotations;
using System;
using JetBrains.Annotations;
using Microsoft.Net.Http.Headers;
using Volo.Abp;
namespace Microsoft.AspNetCore.Http
{
public static class AbpHttpRequestExtensions
{
private const string RequestedWithHeader = "X-Requested-With";
private const string XmlHttpRequest = "XMLHttpRequest";
public static bool IsAjax([NotNull]this HttpRequest request)
{
Check.NotNull(request, nameof(request));
if (request.Headers == null)
{
return false;
}
return request.Headers[RequestedWithHeader] == XmlHttpRequest;
return string.Equals(request.Query[HeaderNames.XRequestedWith], "XMLHttpRequest", StringComparison.Ordinal) ||
string.Equals(request.Headers[HeaderNames.XRequestedWith], "XMLHttpRequest", StringComparison.Ordinal);
}
public static bool CanAccept([NotNull]this HttpRequest request, [NotNull] string contentType)
@ -25,7 +20,7 @@ namespace Microsoft.AspNetCore.Http
Check.NotNull(request, nameof(request));
Check.NotNull(contentType, nameof(contentType));
return request.Headers["Accept"].ToString().Contains(contentType);
return request.Headers[HeaderNames.Accept].ToString().Contains(contentType, StringComparison.OrdinalIgnoreCase);
}
}
}

2
framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore</AssemblyName>
<PackageId>Volo.Abp.AspNetCore</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpAuthorizationExceptionHandlerOptions.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class AbpAuthorizationExceptionHandlerOptions
{
public string AuthenticationScheme { get; set; }
}
}

45
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs

@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http;
@ -58,30 +59,38 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
{
_logger.LogException(exception);
var errorInfoConverter = httpContext.RequestServices.GetRequiredService<IExceptionToErrorInfoConverter>();
var statusCodeFinder = httpContext.RequestServices.GetRequiredService<IHttpExceptionStatusCodeFinder>();
var jsonSerializer = httpContext.RequestServices.GetRequiredService<IJsonSerializer>();
var options = httpContext.RequestServices.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
httpContext.Response.Clear();
httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response);
httpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
await httpContext.Response.WriteAsync(
jsonSerializer.Serialize(
new RemoteServiceErrorResponse(
errorInfoConverter.Convert(exception, options.SendExceptionsDetailsToClients)
)
)
);
await httpContext
.RequestServices
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(
new ExceptionNotificationContext(exception)
);
if (exception is AbpAuthorizationException)
{
await httpContext.RequestServices.GetRequiredService<IAbpAuthorizationExceptionHandler>()
.HandleAsync(exception.As<AbpAuthorizationException>(), httpContext);
}
else
{
var errorInfoConverter = httpContext.RequestServices.GetRequiredService<IExceptionToErrorInfoConverter>();
var statusCodeFinder = httpContext.RequestServices.GetRequiredService<IHttpExceptionStatusCodeFinder>();
var jsonSerializer = httpContext.RequestServices.GetRequiredService<IJsonSerializer>();
var options = httpContext.RequestServices.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
httpContext.Response.Clear();
httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response);
httpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
await httpContext.Response.WriteAsync(
jsonSerializer.Serialize(
new RemoteServiceErrorResponse(
errorInfoConverter.Convert(exception, options.SendExceptionsDetailsToClients)
)
)
);
}
}
private Task ClearCacheHeaders(object state)

67
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultAbpAuthorizationExceptionHandler.cs

@ -0,0 +1,67 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class DefaultAbpAuthorizationExceptionHandler : IAbpAuthorizationExceptionHandler, ITransientDependency
{
public virtual async Task HandleAsync(AbpAuthorizationException exception, HttpContext httpContext)
{
var handlerOptions = httpContext.RequestServices.GetRequiredService<IOptions<AbpAuthorizationExceptionHandlerOptions>>().Value;
var isAuthenticated = httpContext.User.Identity?.IsAuthenticated ?? false;
var authenticationSchemeProvider = httpContext.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
AuthenticationScheme scheme = null;
if (!handlerOptions.AuthenticationScheme.IsNullOrWhiteSpace())
{
scheme = await authenticationSchemeProvider.GetSchemeAsync(handlerOptions.AuthenticationScheme);
if (scheme == null)
{
throw new AbpException($"No authentication scheme named {handlerOptions.AuthenticationScheme} was found.");
}
}
else
{
if (isAuthenticated)
{
scheme = await authenticationSchemeProvider.GetDefaultForbidSchemeAsync();
if (scheme == null)
{
throw new AbpException($"There was no DefaultForbidScheme found.");
}
}
else
{
scheme = await authenticationSchemeProvider.GetDefaultChallengeSchemeAsync();
if (scheme == null)
{
throw new AbpException($"There was no DefaultChallengeScheme found.");
}
}
}
var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name);
if (handler == null)
{
throw new AbpException($"No handler of {scheme.Name} was found.");
}
if (isAuthenticated)
{
await handler.ForbidAsync(null);
}
else
{
await handler.ChallengeAsync(null);
}
}
}
}

11
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IAbpAuthorizationExceptionHandler.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Volo.Abp.Authorization;
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public interface IAbpAuthorizationExceptionHandler
{
Task HandleAsync(AbpAuthorizationException exception, HttpContext httpContext);
}
}

3
framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.Auditing.Contracts/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

21
framework/src/Volo.Abp.Auditing.Contracts/Volo.Abp.Auditing.Contracts.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Auditing.Contracts</AssemblyName>
<PackageId>Volo.Abp.Auditing.Contracts</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
</ItemGroup>
</Project>

9
framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AbpAuditingContractsModule.cs

@ -0,0 +1,9 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Auditing
{
public class AbpAuditingContractsModule : AbpModule
{
}
}

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditedAttribute.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/AuditedAttribute.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/DisableAuditingAttribute.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/DisableAuditingAttribute.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeType.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/EntityChangeType.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingEnabled.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IAuditingEnabled.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/ICreationAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/ICreationAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IDeletionAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IDeletionAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IFullAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IFullAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasCreationTime.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasCreationTime.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasDeletionTime.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasDeletionTime.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IHasModificationTime.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasModificationTime.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMayHaveCreator.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMayHaveCreator.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IModificationAuditedObject.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IModificationAuditedObject.cs

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IMustHaveCreator.cs → framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IMustHaveCreator.cs

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

@ -15,6 +15,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj" />
<ProjectReference Include="..\Volo.Abp.Data\Volo.Abp.Data.csproj" />
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />

3
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs

@ -15,7 +15,8 @@ namespace Volo.Abp.Auditing
typeof(AbpTimingModule),
typeof(AbpSecurityModule),
typeof(AbpThreadingModule),
typeof(AbpMultiTenancyModule)
typeof(AbpMultiTenancyModule),
typeof(AbpAuditingContractsModule)
)]
public class AbpAuditingModule : AbpModule
{

2
framework/src/Volo.Abp.Autofac.WebAssembly/Volo.Abp.Autofac.WebAssembly.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

2
framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

2
framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj

@ -19,7 +19,7 @@
<PackageReference Include="NuGet.Versioning" Version="5.9.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="System.Security.Permissions" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.0-4.final" />
<PackageReference Include="Polly" Version="7.2.1" />
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageReference Include="LibGit2Sharp" Version="0.26.2" />

15
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs

@ -3,7 +3,12 @@ using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.LIbs;
using Volo.Abp.Cli.ServiceProxying;
using Volo.Abp.Cli.ServiceProxying.Angular;
using Volo.Abp.Cli.ServiceProxying.CSharp;
using Volo.Abp.Cli.ServiceProxying.JavaScript;
using Volo.Abp.Domain;
using Volo.Abp.Http;
using Volo.Abp.IdentityModel;
using Volo.Abp.Json;
using Volo.Abp.Json.SystemTextJson;
@ -16,7 +21,8 @@ namespace Volo.Abp.Cli
typeof(AbpDddDomainModule),
typeof(AbpJsonModule),
typeof(AbpIdentityModelModule),
typeof(AbpMinifyModule)
typeof(AbpMinifyModule),
typeof(AbpHttpModule)
)]
public class AbpCliCoreModule : AbpModule
{
@ -58,6 +64,13 @@ namespace Volo.Abp.Cli
options.Commands["create-migration-and-run-migrator"] = typeof(CreateMigrationAndRunMigratorCommand);
options.Commands["install-libs"] = typeof(InstallLibsCommand);
});
Configure<AbpCliServiceProxyOptions>(options =>
{
options.Generators[JavaScriptServiceProxyGenerator.Name] = typeof(JavaScriptServiceProxyGenerator);
options.Generators[AngularServiceProxyGenerator.Name] = typeof(AngularServiceProxyGenerator);
options.Generators[CSharpServiceProxyGenerator.Name] = typeof(CSharpServiceProxyGenerator);
});
}
}
}

86
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Bundling/BundlerBase.cs

@ -4,7 +4,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Text.Json;
using System.Text.Json.Nodes;
using Volo.Abp.Bundling;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Minify;
@ -33,7 +34,7 @@ namespace Volo.Abp.Cli.Bundling
$"{options.BundleName}{FileExtension}");
var bundleFileDefinitions = context.BundleDefinitions.Where(t => t.ExcludeFromBundle == false).ToList();
var fileDefinitionsExcludingFromBundle = context.BundleDefinitions.Where(t => t.ExcludeFromBundle).ToList();
var bundledContent = BundleFiles(options, bundleFileDefinitions);
File.WriteAllText(bundleFilePath, bundledContent);
@ -56,56 +57,61 @@ namespace Volo.Abp.Cli.Bundling
private string BundleFiles(BundleOptions options, List<BundleDefinition> bundleDefinitions)
{
var staticAssetsFilePath = Path.Combine(options.Directory, "bin", "Debug", options.FrameworkVersion,
$"{options.ProjectFileName}.StaticWebAssets.xml");
$"{options.ProjectFileName}.staticwebassets.runtime.json");
if (!File.Exists(staticAssetsFilePath))
{
throw new BundlingException(
"Unable to find static web assets file. You need to build the project to generate static web assets file.");
}
var staticAssetsDefinitions = new XmlDocument();
staticAssetsDefinitions.Load(staticAssetsFilePath);
var builder = new StringBuilder();
foreach (var definition in bundleDefinitions)
using (var file = File.OpenRead(staticAssetsFilePath))
{
string content;
if (definition.Source.StartsWith("_content"))
var jsonDocument = JsonDocument.Parse(file);
var contentRoots = jsonDocument.RootElement.GetProperty("ContentRoots").EnumerateArray()
.Select(x => x.GetString())
.ToList();
var builder = new StringBuilder();
foreach (var definition in bundleDefinitions)
{
var pathFragments = definition.Source.Split('/').ToList();
var basePath = $"{pathFragments[0]}/{pathFragments[1]}";
var node = staticAssetsDefinitions.SelectSingleNode($"//ContentRoot[@BasePath='{basePath}']");
if (node?.Attributes == null)
string content;
if (definition.Source.StartsWith("_content"))
{
throw new AbpException("Not found: " + definition.Source);
var pathFragments = definition.Source.Split('/').ToList();
var basePath = $"{pathFragments[0]}/{pathFragments[1]}";
var path = contentRoots.FirstOrDefault(x => x.IndexOf($"\\{pathFragments[1]}\\", StringComparison.OrdinalIgnoreCase) > 0);
if (path == null)
{
throw new AbpException("Not found: " + definition.Source);
}
var absolutePath = definition.Source.Replace(basePath, path);
content = GetFileContent(absolutePath, options.Minify);
}
var path = node.Attributes["Path"].Value;
var absolutePath = definition.Source.Replace(basePath, path);
content = GetFileContent(absolutePath, options.Minify);
}
else if (definition.Source.StartsWith("_framework"))
{
var slashIndex = definition.Source.IndexOf('/');
var fileName =
definition.Source.Substring(slashIndex + 1, definition.Source.Length - slashIndex - 1);
var filePath =
Path.Combine(PathHelper.GetFrameworkFolderPath(options.Directory, options.FrameworkVersion),
fileName);
content = GetFileContent(filePath, false);
}
else
{
var filePath = Path.Combine(PathHelper.GetWwwRootPath(options.Directory), definition.Source);
content = GetFileContent(filePath, options.Minify);
else if (definition.Source.StartsWith("_framework"))
{
var slashIndex = definition.Source.IndexOf('/');
var fileName =
definition.Source.Substring(slashIndex + 1, definition.Source.Length - slashIndex - 1);
var filePath =
Path.Combine(PathHelper.GetFrameworkFolderPath(options.Directory, options.FrameworkVersion),
fileName);
content = GetFileContent(filePath, false);
}
else
{
var filePath = Path.Combine(PathHelper.GetWwwRootPath(options.Directory), definition.Source);
content = GetFileContent(filePath, options.Minify);
}
content = ProcessBeforeAddingToTheBundle(definition.Source, Path.Combine(options.Directory, "wwwroot"),
content);
builder.AppendLine(content);
}
content = ProcessBeforeAddingToTheBundle(definition.Source, Path.Combine(options.Directory, "wwwroot"),
content);
builder.AppendLine(content);
return builder.ToString();
}
return builder.ToString();
}
private string GetFileContent(string filePath, bool minify)
@ -134,4 +140,4 @@ namespace Volo.Abp.Cli.Bundling
return fileContent;
}
}
}
}

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs

@ -1,4 +1,6 @@
namespace Volo.Abp.Cli
using System;
namespace Volo.Abp.Cli
{
public static class CliUrls
{
@ -32,5 +34,11 @@
{
return $"{NuGetRootPath}{apiKey}/v3/package/{packageId}/index.json";
}
public static string GetApiDefinitionUrl(string url)
{
url = url.EnsureEndsWith('/');
return $"{url}api/abp/api-definition";
}
}
}

29
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs

@ -1,23 +1,40 @@
using Volo.Abp.Cli.Utils;
using System.Text;
using Microsoft.Extensions.Options;
using Volo.Abp.Cli.ServiceProxying;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public class GenerateProxyCommand : ProxyCommandBase
public class GenerateProxyCommand : ProxyCommandBase<GenerateProxyCommand>
{
public const string Name = "generate-proxy";
protected override string CommandName => Name;
protected override string SchematicsCommandName => "proxy-add";
public GenerateProxyCommand(
IOptions<AbpCliServiceProxyOptions> serviceProxyOptions,
IHybridServiceScopeFactory serviceScopeFactory)
: base(serviceProxyOptions, serviceScopeFactory)
{
}
public GenerateProxyCommand(CliService cliService, ICmdHelper cmdHelper)
: base(cliService, cmdHelper)
public override string GetUsageInfo()
{
var sb = new StringBuilder(base.GetUsageInfo());
sb.AppendLine("");
sb.AppendLine("Examples:");
sb.AppendLine("");
sb.AppendLine(" abp generate-proxy -t ng");
sb.AppendLine(" abp generate-proxy -t js -m identity -o Pages/Identity/client-proxies.js -url https://localhost:44302/");
sb.AppendLine(" abp generate-proxy -t csharp --folder MyProxies/InnerFolder -url https://localhost:44302/");
return sb.ToString();
}
public override string GetShortDescription()
{
return "Generates Angular service proxies and DTOs to consume HTTP APIs.";
return "Generates client service proxies and DTOs to consume HTTP APIs.";
}
}
}

165
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProxyCommandBase.cs

@ -4,123 +4,74 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json.Linq;
using NuGet.Versioning;
using Microsoft.Extensions.Options;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Utils;
using Volo.Abp.Cli.ServiceProxying;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public abstract class ProxyCommandBase : IConsoleCommand, ITransientDependency
public abstract class ProxyCommandBase<T> : IConsoleCommand, ITransientDependency where T: IConsoleCommand
{
public CliService CliService { get; }
public ICmdHelper CmdHelper { get; }
public ILogger<HelpCommand> Logger { get; set; }
public ILogger<T> Logger { get; set; }
protected abstract string CommandName { get; }
protected abstract string SchematicsCommandName { get; }
protected AbpCliServiceProxyOptions ServiceProxyOptions { get; }
public ProxyCommandBase(CliService cliService, ICmdHelper cmdHelper)
{
CliService = cliService;
CmdHelper = cmdHelper;
Logger = NullLogger<HelpCommand>.Instance;
}
protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
public ProxyCommandBase(
IOptions<AbpCliServiceProxyOptions> serviceProxyOptions,
IHybridServiceScopeFactory serviceScopeFactory)
{
CheckAngularJsonFile();
await CheckNgSchematicsAsync();
var prompt = commandLineArgs.Options.ContainsKey("p") || commandLineArgs.Options.ContainsKey("prompt");
var defaultValue = prompt ? null : "__default";
var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long) ?? defaultValue;
var apiName = commandLineArgs.Options.GetOrNull(Options.ApiName.Short, Options.ApiName.Long) ?? defaultValue;
var source = commandLineArgs.Options.GetOrNull(Options.Source.Short, Options.Source.Long) ?? defaultValue;
var target = commandLineArgs.Options.GetOrNull(Options.Target.Short, Options.Target.Long) ?? defaultValue;
var commandBuilder = new StringBuilder("npx ng g @abp/ng.schematics:" + SchematicsCommandName);
if (module != null)
{
commandBuilder.Append($" --module {module}");
}
if (apiName != null)
{
commandBuilder.Append($" --api-name {apiName}");
}
if (source != null)
{
commandBuilder.Append($" --source {source}");
}
if (target != null)
{
commandBuilder.Append($" --target {target}");
}
CmdHelper.RunCmd(commandBuilder.ToString());
ServiceScopeFactory = serviceScopeFactory;
ServiceProxyOptions = serviceProxyOptions.Value;
Logger = NullLogger<T>.Instance;
}
private async Task CheckNgSchematicsAsync()
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
var packageJsonPath = $"package.json";
var generateType = commandLineArgs.Options.GetOrNull(Options.GenerateType.Short, Options.GenerateType.Long)?.ToUpper();
if (!File.Exists(packageJsonPath))
if (string.IsNullOrWhiteSpace(generateType))
{
throw new CliUsageException(
"package.json file not found" +
throw new CliUsageException("Option Type is required" +
Environment.NewLine +
GetUsageInfo()
);
GetUsageInfo());
}
var schematicsVersion =
(string) JObject.Parse(File.ReadAllText(packageJsonPath))["devDependencies"]?["@abp/ng.schematics"];
if (schematicsVersion == null)
if (!ServiceProxyOptions.Generators.ContainsKey(generateType))
{
throw new CliUsageException(
"\"@abp/ng.schematics\" NPM package should be installed to the devDependencies before running this command!" +
throw new CliUsageException("Option Type value is invalid" +
Environment.NewLine +
GetUsageInfo()
);
GetUsageInfo());
}
var parseError = SemanticVersion.TryParse(schematicsVersion.TrimStart('~', '^', 'v'), out var semanticSchematicsVersion);
if (parseError)
using (var scope = ServiceScopeFactory.CreateScope())
{
Logger.LogWarning("Couldn't determinate version of \"@abp/ng.schematics\" package.");
return;
}
var generatorType = ServiceProxyOptions.Generators[generateType];
var serviceProxyGenerator = scope.ServiceProvider.GetService(generatorType).As<IServiceProxyGenerator>();
var cliVersion = await CliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly);
if (semanticSchematicsVersion < cliVersion)
{
Logger.LogWarning("\"@abp/ng.schematics\" version is lower than ABP Cli version.");
return;
await serviceProxyGenerator.GenerateProxyAsync(BuildArgs(commandLineArgs));
}
}
private void CheckAngularJsonFile()
private GenerateProxyArgs BuildArgs(CommandLineArgs commandLineArgs)
{
var angularPath = $"angular.json";
if (!File.Exists(angularPath))
{
throw new CliUsageException(
"angular.json file not found. You must run this command in the angular folder." +
Environment.NewLine + Environment.NewLine +
GetUsageInfo()
);
}
var url = commandLineArgs.Options.GetOrNull(Options.Url.Short, Options.Url.Long);
var target = commandLineArgs.Options.GetOrNull(Options.Target.Long);
var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long) ?? "app";
var output = commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long);
var apiName = commandLineArgs.Options.GetOrNull(Options.ApiName.Short, Options.ApiName.Long);
var source = commandLineArgs.Options.GetOrNull(Options.Source.Short, Options.Source.Long);
var workDirectory = commandLineArgs.Options.GetOrNull(Options.WorkDirectory.Short, Options.WorkDirectory.Long) ?? Directory.GetCurrentDirectory();
var folder = commandLineArgs.Options.GetOrNull(Options.Folder.Long);
return new GenerateProxyArgs(CommandName, workDirectory, module, url, output, target, apiName, source, folder, commandLineArgs.Options);
}
public string GetUsageInfo()
public virtual string GetUsageInfo()
{
var sb = new StringBuilder();
@ -131,11 +82,16 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine("");
sb.AppendLine("Options:");
sb.AppendLine("");
sb.AppendLine("-m|--module <module-name> (default: 'app') The name of the backend module you wish to generate proxies for.");
sb.AppendLine("-a|--api-name <module-name> (default: 'default') The name of the API endpoint defined in the /src/environments/environment.ts.");
sb.AppendLine("-s|--source <source-name> (default: 'defaultProject') Angular project name to resolve the root namespace & API definition URL from.");
sb.AppendLine("-t|--target <target-name> (default: 'defaultProject') Angular project name to place generated code in.");
sb.AppendLine("-p|--prompt Asks the options from the command line prompt (for the missing options)");
sb.AppendLine("-m|--module <module-name> (default: 'app') The name of the backend module you wish to generate proxies for.");
sb.AppendLine("-t|--type <generate-type> The name of generate type (csharp, js, ng).");
sb.AppendLine("-wd|--working-directory <directory-path> Execution directory.");
sb.AppendLine("-u|--url <url> API definition URL from.");
sb.AppendLine("-a|--api-name <module-name> (default: 'default') The name of the API endpoint defined in the /src/environments/environment.ts.");
sb.AppendLine("-s|--source <source-name> (default: 'defaultProject') Angular project name to resolve the root namespace & API definition URL from.");
sb.AppendLine("-o|--output <output-name> JavaScript file path or folder to place generated code in.");
sb.AppendLine("-p|--prompt Asks the options from the command line prompt (for the missing options)");
sb.AppendLine("--target <target-name> (default: 'defaultProject') Angular project name to place generated code in.");
sb.AppendLine("--folder <folder-name> (default: 'ClientProxies') Folder name to place generated CSharp code in.");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
@ -152,6 +108,12 @@ namespace Volo.Abp.Cli.Commands
public const string Long = "module";
}
public static class GenerateType
{
public const string Short = "t";
public const string Long = "type";
}
public static class ApiName
{
public const string Short = "a";
@ -163,10 +125,14 @@ namespace Volo.Abp.Cli.Commands
public const string Short = "s";
public const string Long = "source";
}
public static class Output
{
public const string Short = "o";
public const string Long = "output";
}
public static class Target
{
public const string Short = "t";
public const string Long = "target";
}
@ -175,6 +141,23 @@ namespace Volo.Abp.Cli.Commands
public const string Short = "p";
public const string Long = "prompt";
}
public static class Folder
{
public const string Long = "folder";
}
public static class Url
{
public const string Short = "u";
public const string Long = "url";
}
public static class WorkDirectory
{
public const string Short = "wd";
public const string Long = "working-directory";
}
}
}
}

31
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/RemoveProxyCommand.cs

@ -1,25 +1,40 @@
using Volo.Abp.Cli.Utils;
using System.Text;
using Microsoft.Extensions.Options;
using Volo.Abp.Cli.ServiceProxying;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public class RemoveProxyCommand : ProxyCommandBase
public class RemoveProxyCommand : ProxyCommandBase<RemoveProxyCommand>
{
public const string Name = "remove-proxy";
protected override string CommandName => Name;
protected override string SchematicsCommandName => "proxy-remove";
public RemoveProxyCommand(
CliService cliService,
ICmdHelper cmdHelper)
: base(cliService, cmdHelper)
IOptions<AbpCliServiceProxyOptions> serviceProxyOptions,
IHybridServiceScopeFactory serviceScopeFactory)
: base(serviceProxyOptions, serviceScopeFactory)
{
}
public override string GetUsageInfo()
{
var sb = new StringBuilder(base.GetUsageInfo());
sb.AppendLine("");
sb.AppendLine("Examples:");
sb.AppendLine("");
sb.AppendLine(" abp remove-proxy -t ng");
sb.AppendLine(" abp remove-proxy -t js -m identity -o Pages/Identity/client-proxies.js");
sb.AppendLine(" abp remove-proxy -t csharp --folder MyProxies/InnerFolder");
return sb.ToString();
}
public override string GetShortDescription()
{
return "Remove Angular service proxies and DTOs to consume HTTP APIs.";
return "Remove client service proxies and DTOs to consume HTTP APIs.";
}
}
}

15
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/AbpCliServiceProxyOptions.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.Cli.ServiceProxying
{
public class AbpCliServiceProxyOptions
{
public IDictionary<string, Type> Generators { get; }
public AbpCliServiceProxyOptions()
{
Generators = new Dictionary<string, Type>();
}
}
}

115
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs

@ -0,0 +1,115 @@
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using NuGet.Versioning;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxying.Angular
{
public class AngularServiceProxyGenerator : ServiceProxyGeneratorBase<AngularServiceProxyGenerator> , ITransientDependency
{
public const string Name = "NG";
private readonly CliService _cliService;
public AngularServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
CliService cliService) :
base(cliHttpClientFactory, jsonSerializer)
{
_cliService = cliService;
}
public override async Task GenerateProxyAsync(GenerateProxyArgs args)
{
CheckAngularJsonFile();
await CheckNgSchematicsAsync();
var schematicsCommandName = args.CommandName == RemoveProxyCommand.Name ? "proxy-remove" : "proxy-add";
var prompt = args.ExtraProperties.ContainsKey("p") || args.ExtraProperties.ContainsKey("prompt");
var defaultValue = prompt ? null : "__default";
var module = args.Module ?? defaultValue;
var apiName = args.ApiName ?? defaultValue;
var source = args.Source ?? defaultValue;
var target = args.Target ?? defaultValue;
var commandBuilder = new StringBuilder("npx ng g @abp/ng.schematics:" + schematicsCommandName);
if (module != null)
{
commandBuilder.Append($" --module {module}");
}
if (apiName != null)
{
commandBuilder.Append($" --api-name {apiName}");
}
if (source != null)
{
commandBuilder.Append($" --source {source}");
}
if (target != null)
{
commandBuilder.Append($" --target {target}");
}
CmdHelper.RunCmd(commandBuilder.ToString());
}
private async Task CheckNgSchematicsAsync()
{
var packageJsonPath = $"package.json";
if (!File.Exists(packageJsonPath))
{
throw new CliUsageException(
"package.json file not found"
);
}
var schematicsVersion =
(string) JObject.Parse(File.ReadAllText(packageJsonPath))["devDependencies"]?["@abp/ng.schematics"];
if (schematicsVersion == null)
{
throw new CliUsageException(
"\"@abp/ng.schematics\" NPM package should be installed to the devDependencies before running this command!"
);
}
var parseError = SemanticVersion.TryParse(schematicsVersion.TrimStart('~', '^', 'v'), out var semanticSchematicsVersion);
if (parseError)
{
Logger.LogWarning("Couldn't determinate version of \"@abp/ng.schematics\" package.");
return;
}
var cliVersion = await _cliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly);
if (semanticSchematicsVersion < cliVersion)
{
Logger.LogWarning("\"@abp/ng.schematics\" version is lower than ABP Cli version.");
}
}
private static void CheckAngularJsonFile()
{
var angularPath = $"angular.json";
if (!File.Exists(angularPath))
{
throw new CliUsageException(
"angular.json file not found. You must run this command in the angular folder."
);
}
}
}
}

400
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs

@ -0,0 +1,400 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxying.CSharp
{
public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServiceProxyGenerator>, ITransientDependency
{
public const string Name = "CSHARP";
private const string UsingPlaceholder = "<using placeholder>";
private const string MethodPlaceholder = "<method placeholder>";
private const string ClassName = "<className>";
private const string ServiceInterface = "<serviceInterface>";
private const string ServicePostfix = "AppService";
private const string DefaultNamespace = "ClientProxies";
private const string Namespace = "<namespace>";
private const string AppServicePrefix = "Volo.Abp.Application.Services";
private readonly string _clientProxyGeneratedTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
$"{Environment.NewLine}<using placeholder>" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
$"{Environment.NewLine}namespace <namespace>" +
$"{Environment.NewLine}{{" +
$"{Environment.NewLine} [Dependency(ReplaceServices = true)]" +
$"{Environment.NewLine} [ExposeServices(typeof(<serviceInterface>), typeof(<className>))]" +
$"{Environment.NewLine} public partial class <className> : ClientProxyBase<<serviceInterface>>, <serviceInterface>" +
$"{Environment.NewLine} {{" +
$"{Environment.NewLine} <method placeholder>" +
$"{Environment.NewLine} }}" +
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private readonly string _clientProxyTemplate = "// This file is part of <className>, you can customize it here" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
$"{Environment.NewLine}namespace <namespace>" +
$"{Environment.NewLine}{{" +
$"{Environment.NewLine} public partial class <className>" +
$"{Environment.NewLine} {{" +
$"{Environment.NewLine} }}" +
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private readonly List<string> _usingNamespaceList = new()
{
"using System;",
"using System.Threading.Tasks;",
"using Volo.Abp.Application.Dtos;",
"using Volo.Abp.Http.Client;",
"using Volo.Abp.Http.Modeling;",
"using Volo.Abp.DependencyInjection;",
"using Volo.Abp.Http.Client.ClientProxying;"
};
public CSharpServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer) :
base(cliHttpClientFactory, jsonSerializer)
{
}
public override async Task GenerateProxyAsync(GenerateProxyArgs args)
{
CheckWorkDirectory(args.WorkDirectory);
CheckFolder(args.Folder);
if (args.CommandName == RemoveProxyCommand.Name)
{
RemoveClientProxyFile(args);
return;
}
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args);
foreach (var controller in applicationApiDescriptionModel.Modules.Values.SelectMany(x => x.Controllers))
{
if (ShouldGenerateProxy(controller.Value))
{
await GenerateClientProxyFileAsync(args, controller.Value);
}
}
await CreateGenerateProxyJsonFile(args, applicationApiDescriptionModel);
}
private async Task CreateGenerateProxyJsonFile(GenerateProxyArgs args, ApplicationApiDescriptionModel applicationApiDescriptionModel)
{
var folder = args.Folder.IsNullOrWhiteSpace()? DefaultNamespace : args.Folder;
var filePath = Path.Combine(args.WorkDirectory, folder, $"{args.Module}-generate-proxy.json");
using (var writer = new StreamWriter(filePath))
{
await writer.WriteAsync(JsonSerializer.Serialize(applicationApiDescriptionModel, indented: true));
}
}
private void RemoveClientProxyFile(GenerateProxyArgs args)
{
var folder = args.Folder.IsNullOrWhiteSpace()? DefaultNamespace : args.Folder;
var folderPath = Path.Combine(args.WorkDirectory, folder);
if (Directory.Exists(folderPath))
{
Directory.Delete(folderPath, true);
}
Logger.LogInformation($"Delete {GetLoggerOutputPath(folderPath, args.WorkDirectory)}");
}
private async Task GenerateClientProxyFileAsync(
GenerateProxyArgs args,
ControllerApiDescriptionModel controllerApiDescription)
{
var folder = args.Folder.IsNullOrWhiteSpace()? DefaultNamespace : args.Folder;
var appServiceTypeFullName = controllerApiDescription.Interfaces.Last().Type;
var appServiceTypeName = appServiceTypeFullName.Split('.').Last();
var clientProxyName = $"{controllerApiDescription.ControllerName}ClientProxy";
var rootNamespace = $"{GetTypeNamespace(controllerApiDescription.Type)}.{folder.Replace('/', '.')}";
var clientProxyBuilder = new StringBuilder(_clientProxyTemplate);
clientProxyBuilder.Replace(ClassName, clientProxyName);
clientProxyBuilder.Replace(Namespace, rootNamespace);
var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.cs");
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
if (!File.Exists(filePath))
{
using (var writer = new StreamWriter(filePath))
{
await writer.WriteAsync(clientProxyBuilder.ToString());
}
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}");
}
await GenerateClientProxyGeneratedFileAsync(
args,
controllerApiDescription,
clientProxyName,
appServiceTypeName,
appServiceTypeFullName,
rootNamespace,
folder);
}
private async Task GenerateClientProxyGeneratedFileAsync(
GenerateProxyArgs args,
ControllerApiDescriptionModel controllerApiDescription,
string clientProxyName,
string appServiceTypeName,
string appServiceTypeFullName,
string rootNamespace,
string folder)
{
var clientProxyBuilder = new StringBuilder(_clientProxyGeneratedTemplate);
var usingNamespaceList = new List<string>(_usingNamespaceList)
{
$"using {GetTypeNamespace(appServiceTypeFullName)};"
};
clientProxyBuilder.Replace(ClassName, clientProxyName);
clientProxyBuilder.Replace(Namespace, rootNamespace);
clientProxyBuilder.Replace(ServiceInterface, appServiceTypeName);
foreach (var action in controllerApiDescription.Actions.Values)
{
if (!ShouldGenerateMethod(appServiceTypeFullName, action))
{
continue;
}
GenerateMethod(action, clientProxyBuilder, usingNamespaceList);
}
foreach (var usingNamespace in usingNamespaceList)
{
clientProxyBuilder.Replace($"{UsingPlaceholder}", $"{usingNamespace}{Environment.NewLine}{UsingPlaceholder}");
}
clientProxyBuilder.Replace($"{Environment.NewLine}{UsingPlaceholder}", string.Empty);
clientProxyBuilder.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty);
var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.Generated.cs");
using (var writer = new StreamWriter(filePath))
{
await writer.WriteAsync(clientProxyBuilder.ToString());
Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}");
}
}
private void GenerateMethod(
ActionApiDescriptionModel action,
StringBuilder clientProxyBuilder,
List<string> usingNamespaceList)
{
var methodBuilder = new StringBuilder();
var returnTypeName = GetRealTypeName(usingNamespaceList, action.ReturnValue.Type);
if(!action.Name.EndsWith("Async"))
{
GenerateSynchronizationMethod(action, returnTypeName, methodBuilder, usingNamespaceList);
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}");
return;
}
GenerateAsynchronousMethod(action, returnTypeName, methodBuilder, usingNamespaceList);
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}");
}
private void GenerateSynchronizationMethod(ActionApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList)
{
methodBuilder.AppendLine($"public virtual {returnTypeName} {action.Name}(<args>)");
foreach (var parameter in action.Parameters.GroupBy(x => x.Name).Select( x=> x.First()))
{
methodBuilder.Replace("<args>", $"{GetRealTypeName(usingNamespaceList, parameter.Type)} {parameter.Name}, <args>");
}
methodBuilder.Replace("<args>", string.Empty);
methodBuilder.Replace(", )", ")");
methodBuilder.AppendLine(" {");
methodBuilder.AppendLine(" //Client Proxy does not support the synchronization method, you should always use asynchronous methods as a best practice");
methodBuilder.AppendLine(" throw new System.NotImplementedException(); ");
methodBuilder.AppendLine(" }");
}
private void GenerateAsynchronousMethod(
ActionApiDescriptionModel action,
string returnTypeName,
StringBuilder methodBuilder,
List<string> usingNamespaceList)
{
var returnSign = returnTypeName == "void" ? "Task": $"Task<{returnTypeName}>";
methodBuilder.AppendLine($"public virtual async {returnSign} {action.Name}(<args>)");
foreach (var parameter in action.ParametersOnMethod)
{
methodBuilder.Replace("<args>", $"{GetRealTypeName(usingNamespaceList, parameter.Type)} {parameter.Name}, <args>");
}
methodBuilder.Replace("<args>", string.Empty);
methodBuilder.Replace(", )", ")");
methodBuilder.AppendLine(" {");
if (returnTypeName == "void")
{
methodBuilder.AppendLine($" await RequestAsync(nameof({action.Name}), <args>);");
}
else
{
methodBuilder.AppendLine($" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), <args>);");
}
foreach (var parameter in action.ParametersOnMethod)
{
methodBuilder.Replace("<args>", $"{parameter.Name}, <args>");
}
methodBuilder.Replace("<args>", string.Empty);
methodBuilder.Replace(", )", ")");
methodBuilder.AppendLine(" }");
}
private bool ShouldGenerateProxy(ControllerApiDescriptionModel controllerApiDescription)
{
if (!controllerApiDescription.Interfaces.Any())
{
return false;
}
var serviceInterface = controllerApiDescription.Interfaces.Last();
return serviceInterface.Type.EndsWith(ServicePostfix);
}
private bool ShouldGenerateMethod(string appServiceTypeName, ActionApiDescriptionModel action)
{
return action.ImplementFrom.StartsWith(AppServicePrefix) || action.ImplementFrom.StartsWith(appServiceTypeName);
}
private string GetTypeNamespace(string typeFullName)
{
return typeFullName.Substring(0, typeFullName.LastIndexOf('.'));
}
private string GetRealTypeName(List<string> usingNamespaceList, string typeName)
{
var filter = new []{"<", ",", ">"};
var stringBuilder = new StringBuilder();
var typeNames = typeName.Split('.');
if (typeNames.All(x => !filter.Any(x.Contains)))
{
AddUsingNamespace(usingNamespaceList, typeName);
return NormalizeTypeName(typeNames.Last());
}
var fullName = string.Empty;
foreach (var item in typeNames)
{
if (filter.Any(x => item.Contains(x)))
{
AddUsingNamespace(usingNamespaceList, $"{fullName}.{item}".TrimStart('.'));
fullName = string.Empty;
if (item.Contains('<') || item.Contains(','))
{
stringBuilder.Append(item.Substring(0, item.IndexOf(item.Contains('<') ? '<' : ',')+1));
fullName = item.Substring(item.IndexOf(item.Contains('<') ? '<' : ',') + 1);
}
else
{
stringBuilder.Append(item);
}
}
else
{
fullName = $"{fullName}.{item}";
}
}
return stringBuilder.ToString();
}
private void AddUsingNamespace(List<string> usingNamespaceList, string typeName)
{
var rootNamespace = $"using {GetTypeNamespace(typeName)};";
if (usingNamespaceList.Contains(rootNamespace))
{
return;
}
usingNamespaceList.Add(rootNamespace);
}
private string NormalizeTypeName(string typeName)
{
var nullable = string.Empty;
if (typeName.EndsWith("?"))
{
typeName = typeName.TrimEnd('?');
nullable = "?";
}
typeName = typeName switch
{
"Void" => "void",
"Boolean" => "bool",
"String" => "string",
"Int32" => "int",
"Int64" => "long",
"Double" => "double",
"Object" => "object",
"Byte" => "byte",
"Char" => "char",
_ => typeName
};
return $"{typeName}{nullable}";
}
private void CheckWorkDirectory(string directory)
{
if (!Directory.Exists(directory))
{
throw new CliUsageException("Specified directory does not exist.");
}
var projectFiles = Directory.GetFiles(directory, "*HttpApi.Client.csproj");
if (!projectFiles.Any())
{
throw new CliUsageException(
"No project file found in the directory. The working directory must have a HttpApi.Client project file.");
}
}
private void CheckFolder(string folder)
{
if (!folder.IsNullOrWhiteSpace() && Path.HasExtension(folder))
{
throw new CliUsageException("Option folder should be a directory.");
}
}
}
}

55
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/GenerateProxyArgs.cs

@ -0,0 +1,55 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Cli.ServiceProxying
{
public class GenerateProxyArgs
{
[NotNull]
public string CommandName { get; }
[NotNull]
public string WorkDirectory { get; }
public string Module { get; }
public string Url { get; }
public string Output { get; }
public string Target { get; }
public string ApiName { get; }
public string Source { get; }
public string Folder { get; }
[NotNull]
public Dictionary<string, string> ExtraProperties { get; set; }
public GenerateProxyArgs(
[NotNull] string commandName,
[NotNull] string workDirectory,
string module,
string url,
string output,
string target,
string apiName,
string source,
string folder,
Dictionary<string, string> extraProperties = null)
{
CommandName = Check.NotNullOrWhiteSpace(commandName, nameof(commandName));
WorkDirectory = Check.NotNullOrWhiteSpace(workDirectory, nameof(workDirectory));
Module = module;
Url = url;
Output = output;
Target = target;
ApiName = apiName;
Source = source;
Folder = folder;
ExtraProperties = extraProperties ?? new Dictionary<string, string>();
}
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/IServiceProxyGenerator.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.Cli.ServiceProxying
{
public interface IServiceProxyGenerator
{
Task GenerateProxyAsync(GenerateProxyArgs args);
}
}

90
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/JavaScript/JavaScriptServiceProxyGenerator.cs

@ -0,0 +1,90 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.ProxyScripting.Generators.JQuery;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxying.JavaScript
{
public class JavaScriptServiceProxyGenerator : ServiceProxyGeneratorBase<JavaScriptServiceProxyGenerator>, ITransientDependency
{
public const string Name = "JS";
private const string EventTriggerScript = "abp.event.trigger('abp.serviceProxyScriptInitialized');";
private const string DefaultOutput = "wwwroot/client-proxies";
private readonly JQueryProxyScriptGenerator _jQueryProxyScriptGenerator;
public JavaScriptServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
JQueryProxyScriptGenerator jQueryProxyScriptGenerator) :
base(cliHttpClientFactory, jsonSerializer)
{
_jQueryProxyScriptGenerator = jQueryProxyScriptGenerator;
}
public override async Task GenerateProxyAsync(GenerateProxyArgs args)
{
CheckWorkDirectory(args.WorkDirectory);
var output = Path.Combine(args.WorkDirectory, DefaultOutput, $"{args.Module}-proxy.js");
if (!args.Output.IsNullOrWhiteSpace())
{
output = args.Output.EndsWith(".js") ? Path.Combine(args.WorkDirectory, args.Output) : Path.Combine(args.WorkDirectory, Path.GetDirectoryName(args.Output), $"{args.Module}-proxy.js");
}
if (args.CommandName == RemoveProxyCommand.Name)
{
RemoveProxy(args, output);
return;
}
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args);
var script = RemoveInitializedEventTrigger(_jQueryProxyScriptGenerator.CreateScript(applicationApiDescriptionModel));
Directory.CreateDirectory(Path.GetDirectoryName(output));
using (var writer = new StreamWriter(output))
{
await writer.WriteAsync(script);
}
Logger.LogInformation($"Create {GetLoggerOutputPath(output, args.WorkDirectory)}");
}
private void RemoveProxy(GenerateProxyArgs args, string filePath)
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}
Logger.LogInformation($"Delete {GetLoggerOutputPath(filePath, args.WorkDirectory)}");
}
private static void CheckWorkDirectory(string directory)
{
if (!Directory.Exists(directory))
{
throw new CliUsageException("Specified directory does not exist.");
}
var projectFiles = Directory.GetFiles(directory, "*.csproj");
if (!projectFiles.Any())
{
throw new CliUsageException(
"No project file found in the directory. The working directory must have a Web project file.");
}
}
private static string RemoveInitializedEventTrigger(string script)
{
return script.Replace(EventTriggerScript, string.Empty);
}
}
}

56
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/ServiceProxyGeneratorBase.cs

@ -0,0 +1,56 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Http;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxying
{
public abstract class ServiceProxyGeneratorBase<T> : IServiceProxyGenerator where T: IServiceProxyGenerator
{
public IJsonSerializer JsonSerializer { get; }
public CliHttpClientFactory CliHttpClientFactory { get; }
public ILogger<T> Logger { get; set; }
protected ServiceProxyGeneratorBase(CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer)
{
CliHttpClientFactory = cliHttpClientFactory;
JsonSerializer = jsonSerializer;
Logger = NullLogger<T>.Instance;
}
public abstract Task GenerateProxyAsync(GenerateProxyArgs args);
protected virtual async Task<ApplicationApiDescriptionModel> GetApplicationApiDescriptionModelAsync(GenerateProxyArgs args)
{
Check.NotNull(args.Url, nameof(args.Url));
var client = CliHttpClientFactory.CreateClient();
var apiDefinitionResult = await client.GetStringAsync(CliUrls.GetApiDefinitionUrl(args.Url));
var apiDefinition = JsonSerializer.Deserialize<ApplicationApiDescriptionModel>(apiDefinitionResult);
var moduleDefinition = apiDefinition.Modules.FirstOrDefault(x => string.Equals(x.Key, args.Module, StringComparison.CurrentCultureIgnoreCase)).Value;
if (moduleDefinition == null)
{
throw new CliUsageException($"Module name: {args.Module} is invalid");
}
var apiDescriptionModel = ApplicationApiDescriptionModel.Create();
apiDescriptionModel.AddModule(moduleDefinition);
return apiDescriptionModel;
}
protected string GetLoggerOutputPath(string path, string workDirectory)
{
return path.Replace(workDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar);
}
}
}

2
framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj

@ -5,7 +5,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>abp</ToolCommandName>
<RootNamespace />

3
framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj

@ -14,7 +14,6 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="System.Collections.Immutable" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftPackageVersion)" />
@ -25,7 +24,7 @@
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="System.Text.Encodings.Web" Version="5.0.1" />
<PackageReference Include="System.Text.Encodings.Web" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.9" />
<PackageReference Include="System.Linq.Queryable" Version="4.3.0" />

2
framework/src/Volo.Abp.Data/Volo/Abp/Data/ISoftDelete.cs → framework/src/Volo.Abp.Core/Volo/Abp/ISoftDelete.cs

@ -1,4 +1,4 @@
namespace Volo.Abp //TODO: Change namespace to Volo.Abp.Data
namespace Volo.Abp
{
/// <summary>
/// Used to standardize soft deleting entities.

2
framework/src/Volo.Abp.Dapper/Volo.Abp.Dapper.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.Dapper</AssemblyName>
<PackageId>Volo.Abp.Dapper</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

4
framework/src/Volo.Abp.Data/Volo/Abp/Data/DataFilter.cs

@ -44,7 +44,7 @@ namespace Volo.Abp.Data
{
return _filters.GetOrAdd(
typeof(TFilter),
() => _serviceProvider.GetRequiredService<IDataFilter<TFilter>>()
factory:() => _serviceProvider.GetRequiredService<IDataFilter<TFilter>>()
) as IDataFilter<TFilter>;
}
}
@ -105,4 +105,4 @@ namespace Volo.Abp.Data
_filter.Value = _options.DefaultStates.GetOrDefault(typeof(TFilter))?.Clone() ?? new DataFilterState(true);
}
}
}
}

2
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo.Abp.Ddd.Application.Contracts.csproj

@ -20,7 +20,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
<ProjectReference Include="..\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
</ItemGroup>

4
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/AbpDddApplicationContractsModule.cs

@ -7,8 +7,8 @@ using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Application
{
[DependsOn(
typeof(AbpAuditingModule),
typeof(AbpLocalizationModule)
typeof(AbpLocalizationModule),
typeof(AbpAuditingContractsModule)
)]
public class AbpDddApplicationContractsModule : AbpModule
{

1
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleAuditedEntityDto.cs

@ -1,6 +1,5 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
namespace Volo.Abp.Application.Dtos
{

1
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleAuditedEntityWithUserDto.cs

@ -1,6 +1,5 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
namespace Volo.Abp.Application.Dtos
{

1
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleCreationAuditedEntityDto.cs

@ -1,6 +1,5 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
namespace Volo.Abp.Application.Dtos
{

1
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleCreationAuditedEntityWithUserDto.cs

@ -1,6 +1,5 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
namespace Volo.Abp.Application.Dtos
{

1
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleFullAuditedEntityDto.cs

@ -1,6 +1,5 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
namespace Volo.Abp.Application.Dtos
{

1
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleFullAuditedEntityWithUserDto.cs

@ -1,6 +1,5 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
namespace Volo.Abp.Application.Dtos
{

2
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.EntityFrameworkCore.MySQL</AssemblyName>
<PackageId>Volo.Abp.EntityFrameworkCore.MySQL</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.EntityFrameworkCore.Oracle.Devart</AssemblyName>
<PackageId>Volo.Abp.EntityFrameworkCore.Oracle.Devart</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.EntityFrameworkCore.Oracle</AssemblyName>
<PackageId>Volo.Abp.EntityFrameworkCore.Oracle</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

2
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Volo.Abp.EntityFrameworkCore.PostgreSql</AssemblyName>
<PackageId>Volo.Abp.EntityFrameworkCore.PostgreSql</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save