Browse Source

Merge branch 'dev' into studio-dev

pull/10363/head
Yunus Emre Kalkan 4 years ago
parent
commit
4b644f7b99
  1. 2
      docs/en/Migration-Guides/Abp-5_0-Angular.md
  2. 249
      docs/en/UI/Angular/Chart-Component.md
  3. 46
      docs/en/UI/Angular/Permission-Management.md
  4. BIN
      docs/en/UI/Angular/images/bar-chart.png
  5. BIN
      docs/en/UI/Angular/images/doughnut-chart.png
  6. BIN
      docs/en/UI/Angular/images/pie-chart.png
  7. BIN
      docs/en/UI/Angular/images/radar-chart.gif
  8. 4
      docs/en/docs-nav.json
  9. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/AbpTenantController.cs
  10. 105
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerBase.cs
  11. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationController.cs
  12. 36
      framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs
  13. 53
      framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaEventErrorHandler.cs
  14. 42
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
  15. 47
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqEventErrorHandler.cs
  16. 10
      framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/AbpEventBusRebusModule.cs
  17. 2
      framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs
  18. 32
      framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusEventErrorHandler.cs
  19. 8
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
  20. 22
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusOptions.cs
  21. 9
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusRetryStrategyOptions.cs
  22. 10
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs
  23. 14
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
  24. 76
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventErrorHandlerBase.cs
  25. 38
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventExecutionErrorContext.cs
  26. 9
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventErrorHandler.cs
  27. 13
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs
  28. 60
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventErrorHandler.cs
  29. 2
      framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/IKafkaMessageConsumerFactory.cs
  30. 29
      framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumer.cs
  31. 4
      framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumerFactory.cs
  32. 6
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ExchangeDeclareConfiguration.cs
  33. 6
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/QueueDeclareConfiguration.cs
  34. 38
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs
  35. 12
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBusTestModule.cs
  36. 72
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_Exception_Handler_Tests.cs
  37. 12
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MyExceptionHandleEventData.cs
  38. 2
      modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/AccountController.cs
  39. 2
      modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
  40. 2
      modules/blogging/src/Volo.Blogging.Admin.HttpApi/Volo/Blogging/Admin/BlogManagementController.cs
  41. 2
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs
  42. 2
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs
  43. 2
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs
  44. 2
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs
  45. 2
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/TagsController.cs
  46. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/CmsKitAdminController.cs
  47. 2
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/CmsKitControllerBase.cs
  48. 2
      modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs
  49. 2
      modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/ProjectsAdminController.cs
  50. 2
      modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Projects/DocsProjectController.cs
  51. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs
  52. 2
      modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs
  53. 2
      modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserController.cs
  54. 2
      modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs
  55. 2
      modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/ProfileController.cs
  56. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs
  57. 2
      modules/setting-management/src/Volo.Abp.SettingManagement.HttpApi/Volo/Abp/SettingManagement/EmailSettingsController.cs
  58. 2
      modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs
  59. 2
      npm/ng-packs/packages/components/chart.js/src/chart.component.ts
  60. 26
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html
  61. 34
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts
  62. 7
      npm/ng-packs/packages/theme-shared/extensions/src/lib/models/entity-props.ts
  63. 2
      npm/ng-packs/packages/theme-shared/extensions/src/lib/tokens/extensions.token.ts
  64. 23
      npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb-items/breadcrumb-items.component.html
  65. 9
      npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb-items/breadcrumb-items.component.ts
  66. 14
      npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html
  67. 13
      npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts
  68. 2
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  69. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/Controllers/MyProjectNameController.cs
  70. 2
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyProjectNameController.cs

2
docs/en/Migration-Guides/Abp-5_0-Angular.md

@ -85,7 +85,7 @@ Following proxies have been affected:
### ChartComponent
`ChartComponent` has moved from `@abp/ng.theme.shared` to `@abp/ng.components/chart.js`. To use the component, you need to import the `ChartModule` to your module as follows:
[`ChartComponent`](../UI/Angular/Chart-Component.md) has moved from `@abp/ng.theme.shared` to `@abp/ng.components/chart.js`. To use the component, you need to import the `ChartModule` to your module as follows:
```ts
import { ChartModule } from '@abp/ng.components/chart.js';

249
docs/en/UI/Angular/Chart-Component.md

@ -0,0 +1,249 @@
# Chart Component
ABP Chart component exposed by `@abp/ng.components/chart.js` is based on [`charts.js`](https://www.chartjs.org/) v3+. You don't need to install the `chart.js` package. Since the `@abp/ng.components` is dependent on the `chart.js`, the package is already installed in your project.
> Chart component loads `chart.js` script lazy. So it does not increase the bundle size.
## How to Use
First of all, need to import the `ChartModule` to your feature module as follows:
```ts
// your-feature.module.ts
import { ChartModule } from '@abp/ng.components/chart.js';
import { ChartDemoComponent } from './chart-demo.component';
@NgModule({
imports: [
ChartModule,
// ...
],
declarations: [ChartDemoComponent],
// ...
})
export class YourFeatureModule {}
```
Then, `abp-chart` component can be used. See an example:
```ts
// chart-demo.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-chart-demo',
template: ` <abp-chart type="pie" [data]="data"></abp-chart> `,
})
export class ChartDemoComponent {
data = {
labels: ['Data 1', 'Data 2', 'Data 3'],
datasets: [
{
label: 'Dataset 1',
data: [40, 15, 45],
backgroundColor: ['#ff7675', '#fdcb6e', '#0984e3'],
},
],
};
}
```
> **Important Note**: Changing the chart data without creating a new data instance does not trigger change detection. In order to chart to redraw itself, a new data object needs to be created.
See the result:
![pie-chart](./images/pie-chart.png)
## Examples
### Doughnut
```ts
import { Component } from '@angular/core';
@Component({
selector: 'app-chart-demo',
template: `
<abp-chart
type="doughnut"
[data]="data"
[options]="options"
width="400px"
height="400px"
></abp-chart>
`,
})
export class ChartDemoComponent {
data = {
labels: ['Data 1', 'Data 2', 'Data 3'],
datasets: [
{
label: 'Dataset 1',
data: [40, 15, 45],
backgroundColor: ['#a0e6c3', '#f0ea4c', '#5b9dc3'],
},
],
};
options = {
plugins: {
title: {
display: true,
text: 'Doughnut Chart',
fontSize: 16,
},
legend: {
position: 'bottom',
},
},
};
}
```
Result:
![Doughnut Chart](./images/doughnut-chart.png)
### Bar
```ts
import { Component } from '@angular/core';
@Component({
selector: 'app-chart-demo',
template: `
<abp-chart
type="bar"
[data]="data"
width="400px"
height="400px"
></abp-chart>
`,
})
export class ChartDemoComponent {
data = {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [
{
label: 'First dataset',
backgroundColor: '#42A5F5',
data: [65, 59, 80, 81, 56, 55, 40],
},
{
label: 'Second dataset',
backgroundColor: '#FFA726',
data: [28, 48, 40, 19, 86, 27, 90],
},
],
};
}
```
Result:
![Bar Chart](./images/bar-chart.png)
### Radar
```ts
import { Component } from '@angular/core';
@Component({
selector: 'app-chart-demo',
template: `
<abp-chart
type="radar"
[data]="data"
width="400px"
height="400px"
></abp-chart>
<button class="btn btn-primary-outline mt-4" (click)="addDataset()">
Add dataset
</button>
`,
})
export class ChartDemoComponent {
data = {
labels: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
datasets: [
{
label: 'Dataset 1',
backgroundColor: 'rgba(179,181,198,0.2)',
borderColor: 'rgba(179,181,198,1)',
data: [65, 59, 90, 81, 56, 55, 40, 35, 82, 51, 62, 95],
},
{
label: 'Dataset 2',
backgroundColor: 'rgba(255,99,132,0.2)',
borderColor: 'rgba(255,99,132,1)',
data: [28, 48, 40, 58, 96, 27, 100, 44, 85, 77, 71, 39],
},
],
};
addDataset() {
this.data = {
...this.data,
datasets: [
...this.data.datasets,
{
label: 'Dataset 3',
backgroundColor: 'rgba(54,162,235,0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
data: [90, 95, 98, 91, 99, 96, 89, 95, 98, 93, 92, 90],
},
],
};
}
}
```
Result:
![Bar Chart](./images/radar-chart.gif)
See the [`chart.js` samples](https://www.chartjs.org/docs/latest/samples) for more examples.
## API
### `abp-chart`
#### Properties
| Name | Description | Type | Default |
| --------------- | ---------------------------------------------------------------- | ----------------------- | ------- |
| `[type]` | Type of the chart. | `string` | null |
| `[data]` | Chart data to display | `any` | null |
| `[options]` | Chart options to customize | `any` | null |
| `[plugins]` | Chart plugins to customize behaviour | `any` | null |
| `[width]` | Witdh of the chart | `string` | null |
| `[height]` | Height of the chart | `string` | null |
| `[responsive]` | Whether the chart is responsive | `boolean` | true |
| `(dataSelect)` | A callback that executes when an element on the chart is clicked | `EventEmitter<any>` | - |
| `(initialized)` | A callback that executes when the chart is initialized | `EventEmitter<boolean>` | - |
#### Methods
| Name | Description | Parameters |
| ---------------- | ------------------------------------------------------------------- | ---------- |
| `refresh` | Redraws the chart | - |
| `reinit` | Destroys the chart then creates it again | - |
| `getBase64Image` | Returns a base 64 encoded string of the chart in it's current state | - |
| `generateLegend` | Returns an HTML string of a legend for the chart | - |
| `getCanvas` | Returns the canvas HTML element | - |

46
docs/en/UI/Angular/Permission-Management.md

@ -4,8 +4,6 @@ A permission is a simple policy that is granted or prohibited for a particular u
You can get permission of authenticated user using `getGrantedPolicy` or `getGrantedPolicy$` method of `PermissionService`.
> ConfigState's getGrantedPolicy selector and ConfigStateService's getGrantedPolicy method deprecated. Use permission service's `getGrantedPolicy$` or `getGrantedPolicy`methods instead
You can get permission as boolean value:
```js
@ -77,4 +75,46 @@ const routes: Routes = [
];
```
Granted Policies are stored in the `auth` property of `ConfigState`.
## Customization
In some cases, a custom permission management may be needed. All you need to do is to replace the service with your own. Here is how to achieve this:
- First, create a service of your own. Let's call it `CustomPermissionService` and extend `PermissionService` from `@abp/ng.core` as follows:
```js
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class CustomPermissionService extends PermissionService {
constructor(configStateService: ConfigStateService) {
super(configStateService);
}
// This is an example to show how to override the methods
getGrantedPolicy$(key: string) {
return super.getGrantedPolicy$(key);
}
}
```
- Then, in `app.module.ts`, provide this service as follows:
```js
@NgModule({
// ...
providers: [
// ...
{
provide: PermissionService,
useExisting: CustomPermissionService,
},
],
// ...
})
export class AppModule {}
```
That's it. Now, when a directive/guard asks for `PermissionService` from angular, it will inject your service.

BIN
docs/en/UI/Angular/images/bar-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/en/UI/Angular/images/doughnut-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/UI/Angular/images/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/UI/Angular/images/radar-chart.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

4
docs/en/docs-nav.json

@ -1079,6 +1079,10 @@
{
"text": "Page",
"path": "UI/Angular/Page-Component.md"
},
{
"text": "Chart",
"path": "UI/Angular/Chart-Component.md"
}
]
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/AbpTenantController.cs

@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
@ -10,7 +11,7 @@ namespace Pages.Abp.MultiTenancy
[Area("abp")]
[RemoteService(Name = "abp")]
[Route("api/abp/multi-tenancy")]
public class AbpTenantController : AbpController, IAbpTenantAppService
public class AbpTenantController : AbpControllerBase, IAbpTenantAppService
{
private readonly IAbpTenantAppService _abpTenantAppService;

105
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerBase.cs

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Mvc.Validation;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.Guids;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
using Volo.Abp.Users;
namespace Volo.Abp.AspNetCore.Mvc
{
public abstract class AbpControllerBase : ControllerBase, IAvoidDuplicateCrossCuttingConcerns
{
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
protected IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>();
protected Type ObjectMapperContext { get; set; }
protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetService<IObjectMapper>(provider =>
ObjectMapperContext == null
? provider.GetRequiredService<IObjectMapper>()
: (IObjectMapper) provider.GetRequiredService(typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext)));
protected IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetService<IGuidGenerator>(SimpleGuidGenerator.Instance);
protected ILoggerFactory LoggerFactory => LazyServiceProvider.LazyGetRequiredService<ILoggerFactory>();
protected ILogger Logger => LazyServiceProvider.LazyGetService<ILogger>(provider => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance);
protected ICurrentUser CurrentUser => LazyServiceProvider.LazyGetRequiredService<ICurrentUser>();
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
protected IAuthorizationService AuthorizationService => LazyServiceProvider.LazyGetRequiredService<IAuthorizationService>();
protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current;
protected IClock Clock => LazyServiceProvider.LazyGetRequiredService<IClock>();
protected IModelStateValidator ModelValidator => LazyServiceProvider.LazyGetRequiredService<IModelStateValidator>();
protected IFeatureChecker FeatureChecker => LazyServiceProvider.LazyGetRequiredService<IFeatureChecker>();
protected IStringLocalizerFactory StringLocalizerFactory => LazyServiceProvider.LazyGetRequiredService<IStringLocalizerFactory>();
protected IStringLocalizer L
{
get
{
if (_localizer == null)
{
_localizer = CreateLocalizer();
}
return _localizer;
}
}
private IStringLocalizer _localizer;
protected Type LocalizationResource
{
get => _localizationResource;
set
{
_localizationResource = value;
_localizer = null;
}
}
private Type _localizationResource = typeof(DefaultResource);
public List<string> AppliedCrossCuttingConcerns { get; } = new List<string>();
protected virtual IStringLocalizer CreateLocalizer()
{
if (LocalizationResource != null)
{
return StringLocalizerFactory.Create(LocalizationResource);
}
var localizer = StringLocalizerFactory.CreateDefaultOrNull();
if (localizer == null)
{
throw new AbpException($"Set {nameof(LocalizationResource)} or define the default localization resource type (by configuring the {nameof(AbpLocalizationOptions)}.{nameof(AbpLocalizationOptions.DefaultResourceType)}) to be able to use the {nameof(L)} object!");
}
return localizer;
}
protected virtual void ValidateModel()
{
ModelValidator?.Validate(ModelState);
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationController.cs

@ -7,7 +7,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
[Area("abp")]
[RemoteService(Name = "abp")]
[Route("api/abp/application-configuration")]
public class AbpApplicationConfigurationController : AbpController, IAbpApplicationConfigurationAppService
public class AbpApplicationConfigurationController : AbpControllerBase, IAbpApplicationConfigurationAppService
{
private readonly IAbpApplicationConfigurationAppService _applicationConfigurationAppService;
private readonly IAbpAntiForgeryManager _antiForgeryManager;

36
framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs

@ -6,7 +6,6 @@ using System.Threading.Tasks;
using Confluent.Kafka;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;
@ -22,7 +21,6 @@ namespace Volo.Abp.EventBus.Kafka
[ExposeServices(typeof(IDistributedEventBus), typeof(KafkaDistributedEventBus))]
public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDependency
{
protected AbpEventBusOptions AbpEventBusOptions { get; }
protected AbpKafkaEventBusOptions AbpKafkaEventBusOptions { get; }
protected IKafkaMessageConsumerFactory MessageConsumerFactory { get; }
protected IKafkaSerializer Serializer { get; }
@ -30,7 +28,6 @@ namespace Volo.Abp.EventBus.Kafka
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; }
protected ConcurrentDictionary<string, Type> EventTypes { get; }
protected IKafkaMessageConsumer Consumer { get; private set; }
protected string DeadLetterTopicName { get; }
public KafkaDistributedEventBus(
IServiceScopeFactory serviceScopeFactory,
@ -41,26 +38,20 @@ namespace Volo.Abp.EventBus.Kafka
IOptions<AbpDistributedEventBusOptions> abpDistributedEventBusOptions,
IKafkaSerializer serializer,
IProducerPool producerPool,
IEventErrorHandler errorHandler,
IOptions<AbpEventBusOptions> abpEventBusOptions,
IGuidGenerator guidGenerator,
IClock clock)
: base(
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler,
abpDistributedEventBusOptions,
guidGenerator,
clock)
{
AbpKafkaEventBusOptions = abpKafkaEventBusOptions.Value;
AbpEventBusOptions = abpEventBusOptions.Value;
MessageConsumerFactory = messageConsumerFactory;
Serializer = serializer;
ProducerPool = producerPool;
DeadLetterTopicName =
AbpEventBusOptions.DeadLetterName ?? AbpKafkaEventBusOptions.TopicName + "_dead_letter";
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>();
@ -70,7 +61,6 @@ namespace Volo.Abp.EventBus.Kafka
{
Consumer = MessageConsumerFactory.Create(
AbpKafkaEventBusOptions.TopicName,
DeadLetterTopicName,
AbpKafkaEventBusOptions.GroupId,
AbpKafkaEventBusOptions.ConnectionName);
Consumer.OnMessageReceived(ProcessEventAsync);
@ -88,12 +78,12 @@ namespace Volo.Abp.EventBus.Kafka
}
string messageId = null;
if (message.Headers.TryGetLastBytes("messageId", out var messageIdBytes))
{
messageId = System.Text.Encoding.UTF8.GetString(messageIdBytes);
}
if (await AddToInboxAsync(messageId, eventName, eventType, message.Value))
{
return;
@ -101,18 +91,7 @@ namespace Volo.Abp.EventBus.Kafka
var eventData = Serializer.Deserialize(message.Value, eventType);
await TriggerHandlersAsync(eventType, eventData, errorContext =>
{
var retryAttempt = 0;
if (message.Headers.TryGetLastBytes(EventErrorHandlerBase.RetryAttemptKey, out var retryAttemptBytes))
{
retryAttempt = Serializer.Deserialize<int>(retryAttemptBytes);
}
errorContext.EventData = Serializer.Deserialize(message.Value, eventType);
errorContext.SetProperty(EventErrorHandlerBase.HeadersKey, message.Headers);
errorContext.SetProperty(EventErrorHandlerBase.RetryAttemptKey, retryAttempt);
});
await TriggerHandlersAsync(eventType, eventData);
}
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
@ -226,7 +205,7 @@ namespace Volo.Abp.EventBus.Kafka
{
return;
}
var eventData = Serializer.Deserialize(incomingEvent.EventData, eventType);
var exceptions = new List<Exception>();
await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig);
@ -252,11 +231,6 @@ namespace Volo.Abp.EventBus.Kafka
);
}
public virtual async Task PublishToDeadLetterAsync(Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments)
{
await PublishAsync(DeadLetterTopicName, eventType, eventData, headers, headersArguments);
}
private Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments)
{
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
@ -264,7 +238,7 @@ namespace Volo.Abp.EventBus.Kafka
return PublishAsync(topicName, eventName, body, headers, headersArguments);
}
private async Task PublishAsync(string topicName, string eventName, byte[] body, Headers headers, Dictionary<string, object> headersArguments)
{
var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName);

53
framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaEventErrorHandler.cs

@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Confluent.Kafka;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EventBus.Kafka
{
public class KafkaEventErrorHandler : EventErrorHandlerBase, ISingletonDependency
{
protected ILogger<KafkaEventErrorHandler> Logger { get; set; }
public KafkaEventErrorHandler(
IOptions<AbpEventBusOptions> options) : base(options)
{
Logger = NullLogger<KafkaEventErrorHandler>.Instance;
}
protected override async Task RetryAsync(EventExecutionErrorContext context)
{
if (Options.RetryStrategyOptions.IntervalMillisecond > 0)
{
await Task.Delay(Options.RetryStrategyOptions.IntervalMillisecond);
}
context.TryGetRetryAttempt(out var retryAttempt);
await context.EventBus.As<KafkaDistributedEventBus>().PublishAsync(
context.EventType,
context.EventData,
context.GetProperty(HeadersKey).As<Headers>(),
new Dictionary<string, object> {{RetryAttemptKey, ++retryAttempt}});
}
protected override async Task MoveToDeadLetterAsync(EventExecutionErrorContext context)
{
Logger.LogException(
context.Exceptions.Count == 1 ? context.Exceptions.First() : new AggregateException(context.Exceptions),
LogLevel.Error);
await context.EventBus.As<KafkaDistributedEventBus>().PublishToDeadLetterAsync(
context.EventType,
context.EventData,
context.GetProperty(HeadersKey).As<Headers>(),
new Dictionary<string, object> {{"exceptions", context.Exceptions.Select(x => x.ToString()).ToList()}});
}
}
}

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

@ -7,7 +7,6 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;
@ -26,7 +25,6 @@ namespace Volo.Abp.EventBus.RabbitMq
public class RabbitMqDistributedEventBus : DistributedEventBusBase, ISingletonDependency
{
protected AbpRabbitMqEventBusOptions AbpRabbitMqEventBusOptions { get; }
protected AbpEventBusOptions AbpEventBusOptions { get; }
protected IConnectionPool ConnectionPool { get; }
protected IRabbitMqSerializer Serializer { get; }
@ -45,15 +43,12 @@ namespace Volo.Abp.EventBus.RabbitMq
IRabbitMqMessageConsumerFactory messageConsumerFactory,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IEventErrorHandler errorHandler,
IOptions<AbpEventBusOptions> abpEventBusOptions,
IGuidGenerator guidGenerator,
IClock clock)
: base(
serviceScopeFactory,
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler,
distributedEventBusOptions,
guidGenerator,
clock)
@ -61,7 +56,6 @@ namespace Volo.Abp.EventBus.RabbitMq
ConnectionPool = connectionPool;
Serializer = serializer;
MessageConsumerFactory = messageConsumerFactory;
AbpEventBusOptions = abpEventBusOptions.Value;
AbpRabbitMqEventBusOptions = options.Value;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
@ -70,21 +64,17 @@ namespace Volo.Abp.EventBus.RabbitMq
public void Initialize()
{
const string suffix = "_dead_letter";
Consumer = MessageConsumerFactory.Create(
new ExchangeDeclareConfiguration(
AbpRabbitMqEventBusOptions.ExchangeName,
type: "direct",
durable: true,
deadLetterExchangeName: AbpRabbitMqEventBusOptions.ExchangeName + suffix
durable: true
),
new QueueDeclareConfiguration(
AbpRabbitMqEventBusOptions.ClientName,
durable: true,
exclusive: false,
autoDelete: false,
AbpEventBusOptions.DeadLetterName ?? AbpRabbitMqEventBusOptions.ClientName + suffix
autoDelete: false
),
AbpRabbitMqEventBusOptions.ConnectionName
);
@ -104,27 +94,15 @@ namespace Volo.Abp.EventBus.RabbitMq
}
var eventBytes = ea.Body.ToArray();
if (await AddToInboxAsync(ea.BasicProperties.MessageId, eventName, eventType, eventBytes))
{
return;
}
var eventData = Serializer.Deserialize(eventBytes, eventType);
await TriggerHandlersAsync(eventType, eventData, errorContext =>
{
var retryAttempt = 0;
if (ea.BasicProperties.Headers != null &&
ea.BasicProperties.Headers.ContainsKey(EventErrorHandlerBase.RetryAttemptKey))
{
retryAttempt = (int)ea.BasicProperties.Headers[EventErrorHandlerBase.RetryAttemptKey];
}
var eventData = Serializer.Deserialize(eventBytes, eventType);
errorContext.EventData = Serializer.Deserialize(eventBytes, eventType);
errorContext.SetProperty(EventErrorHandlerBase.HeadersKey, ea.BasicProperties);
errorContext.SetProperty(EventErrorHandlerBase.RetryAttemptKey, retryAttempt);
});
await TriggerHandlersAsync(eventType, eventData);
}
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
@ -226,7 +204,7 @@ namespace Volo.Abp.EventBus.RabbitMq
{
return;
}
var eventData = Serializer.Deserialize(incomingEvent.EventData, eventType);
var exceptions = new List<Exception>();
await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig);
@ -235,7 +213,7 @@ namespace Volo.Abp.EventBus.RabbitMq
ThrowOriginalExceptions(eventType, exceptions);
}
}
protected override byte[] Serialize(object eventData)
{
return Serializer.Serialize(eventData);
@ -248,7 +226,7 @@ namespace Volo.Abp.EventBus.RabbitMq
return PublishAsync(eventName, body, properties, headersArguments);
}
protected Task PublishAsync(
string eventName,
byte[] body,
@ -274,7 +252,7 @@ namespace Volo.Abp.EventBus.RabbitMq
{
properties.MessageId = (eventId ?? GuidGenerator.Create()).ToString("N");
}
SetEventMessageHeaders(properties, headersArguments);
channel.BasicPublish(

47
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqEventErrorHandler.cs

@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EventBus.RabbitMq
{
public class RabbitMqEventErrorHandler : EventErrorHandlerBase, ISingletonDependency
{
public RabbitMqEventErrorHandler(
IOptions<AbpEventBusOptions> options)
: base(options)
{
}
protected override async Task RetryAsync(EventExecutionErrorContext context)
{
if (Options.RetryStrategyOptions.IntervalMillisecond > 0)
{
await Task.Delay(Options.RetryStrategyOptions.IntervalMillisecond);
}
context.TryGetRetryAttempt(out var retryAttempt);
await context.EventBus.As<RabbitMqDistributedEventBus>().PublishAsync(
context.EventType,
context.EventData,
context.GetProperty(HeadersKey).As<IBasicProperties>(),
new Dictionary<string, object>
{
{RetryAttemptKey, ++retryAttempt},
{"exceptions", context.Exceptions.Select(x => x.ToString()).ToList()}
});
}
protected override Task MoveToDeadLetterAsync(EventExecutionErrorContext context)
{
ThrowOriginalExceptions(context);
return Task.CompletedTask;
}
}
}

10
framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/AbpEventBusRebusModule.cs

@ -1,6 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Rebus.Handlers;
using Rebus.Retry.Simple;
using Rebus.ServiceProvider;
using Volo.Abp.Modularity;
@ -12,7 +11,6 @@ namespace Volo.Abp.EventBus.Rebus
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var abpEventBusOptions = context.Services.ExecutePreConfiguredActions<AbpEventBusOptions>();
var options = context.Services.ExecutePreConfiguredActions<AbpRebusEventBusOptions>();;
context.Services.AddTransient(typeof(IHandleMessages<>), typeof(RebusDistributedEventHandlerAdapter<>));
@ -24,14 +22,6 @@ namespace Volo.Abp.EventBus.Rebus
context.Services.AddRebus(configure =>
{
if (abpEventBusOptions.RetryStrategyOptions != null)
{
configure.Options(b =>
b.SimpleRetryStrategy(
errorQueueAddress: abpEventBusOptions.DeadLetterName ?? options.InputQueueName + "_dead_letter",
maxDeliveryAttempts: abpEventBusOptions.RetryStrategyOptions.MaxRetryAttempts));
}
options.Configurer?.Invoke(configure);
return configure;
});

2
framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs

@ -36,7 +36,6 @@ namespace Volo.Abp.EventBus.Rebus
IBus rebus,
IOptions<AbpDistributedEventBusOptions> abpDistributedEventBusOptions,
IOptions<AbpRebusEventBusOptions> abpEventBusRebusOptions,
IEventErrorHandler errorHandler,
IRebusSerializer serializer,
IGuidGenerator guidGenerator,
IClock clock) :
@ -44,7 +43,6 @@ namespace Volo.Abp.EventBus.Rebus
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler,
abpDistributedEventBusOptions,
guidGenerator,
clock)

32
framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusEventErrorHandler.cs

@ -1,32 +0,0 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EventBus.Rebus
{
/// <summary>
/// Rebus will automatic retries and error handling: https://github.com/rebus-org/Rebus/wiki/Automatic-retries-and-error-handling
/// </summary>
public class RebusEventErrorHandler : EventErrorHandlerBase, ISingletonDependency
{
public RebusEventErrorHandler(
IOptions<AbpEventBusOptions> options)
: base(options)
{
}
protected override Task RetryAsync(EventExecutionErrorContext context)
{
ThrowOriginalExceptions(context);
return Task.CompletedTask;
}
protected override Task MoveToDeadLetterAsync(EventExecutionErrorContext context)
{
ThrowOriginalExceptions(context);
return Task.CompletedTask;
}
}
}

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

@ -25,14 +25,6 @@ namespace Volo.Abp.EventBus
AddEventHandlers(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpEventBusOptions>(options =>
{
context.Services.ExecutePreConfiguredActions(options);
});
}
private static void AddEventHandlers(IServiceCollection services)
{
var localHandlers = new List<Type>();

22
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusOptions.cs

@ -1,22 +0,0 @@
using System;
namespace Volo.Abp.EventBus
{
public class AbpEventBusOptions
{
public bool EnabledErrorHandle { get; set; }
public Func<Type, bool> ErrorHandleSelector { get; set; }
public string DeadLetterName { get; set; }
public AbpEventBusRetryStrategyOptions RetryStrategyOptions { get; set; }
public void UseRetryStrategy(Action<AbpEventBusRetryStrategyOptions> action = null)
{
EnabledErrorHandle = true;
RetryStrategyOptions = new AbpEventBusRetryStrategyOptions();
action?.Invoke(RetryStrategyOptions);
}
}
}

9
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusRetryStrategyOptions.cs

@ -1,9 +0,0 @@
namespace Volo.Abp.EventBus
{
public class AbpEventBusRetryStrategyOptions
{
public int IntervalMillisecond { get; set; } = 3000;
public int MaxRetryAttempts { get; set; } = 3;
}
}

10
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs

@ -20,15 +20,13 @@ namespace Volo.Abp.EventBus.Distributed
IServiceScopeFactory serviceScopeFactory,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IEventErrorHandler errorHandler,
IOptions<AbpDistributedEventBusOptions> abpDistributedEventBusOptions,
IGuidGenerator guidGenerator,
IClock clock
) : base(
serviceScopeFactory,
currentTenant,
unitOfWorkManager,
errorHandler)
unitOfWorkManager)
{
GuidGenerator = guidGenerator;
Clock = clock;
@ -84,7 +82,7 @@ namespace Volo.Abp.EventBus.Distributed
OutgoingEventInfo outgoingEvent,
OutboxConfig outboxConfig
);
public abstract Task ProcessFromInboxAsync(
IncomingEventInfo incomingEvent,
InboxConfig inboxConfig);
@ -144,7 +142,7 @@ namespace Volo.Abp.EventBus.Distributed
continue;
}
}
await eventInbox.EnqueueAsync(
new IncomingEventInfo(
GuidGenerator.Create(),
@ -163,4 +161,4 @@ namespace Volo.Abp.EventBus.Distributed
protected abstract byte[] Serialize(object eventData);
}
}
}

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

@ -22,18 +22,14 @@ namespace Volo.Abp.EventBus
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected IEventErrorHandler ErrorHandler { get; }
protected EventBusBase(
IServiceScopeFactory serviceScopeFactory,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IEventErrorHandler errorHandler)
IUnitOfWorkManager unitOfWorkManager)
{
ServiceScopeFactory = serviceScopeFactory;
CurrentTenant = currentTenant;
UnitOfWorkManager = unitOfWorkManager;
ErrorHandler = errorHandler;
}
/// <inheritdoc/>
@ -120,7 +116,7 @@ namespace Volo.Abp.EventBus
protected abstract void AddToUnitOfWork(IUnitOfWork unitOfWork, UnitOfWorkEventRecord eventRecord);
public virtual async Task TriggerHandlersAsync(Type eventType, object eventData, Action<EventExecutionErrorContext> onErrorAction = null)
public virtual async Task TriggerHandlersAsync(Type eventType, object eventData)
{
var exceptions = new List<Exception>();
@ -128,9 +124,7 @@ namespace Volo.Abp.EventBus
if (exceptions.Any())
{
var context = new EventExecutionErrorContext(exceptions, eventType, this);
onErrorAction?.Invoke(context);
await ErrorHandler.HandleAsync(context);
ThrowOriginalExceptions(eventType, exceptions);
}
}
@ -162,7 +156,7 @@ namespace Volo.Abp.EventBus
}
}
}
protected void ThrowOriginalExceptions(Type eventType, List<Exception> exceptions)
{
if (exceptions.Count == 1)

76
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventErrorHandlerBase.cs

@ -1,76 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace Volo.Abp.EventBus
{
public abstract class EventErrorHandlerBase : IEventErrorHandler
{
public const string HeadersKey = "headers";
public const string RetryAttemptKey = "retryAttempt";
protected AbpEventBusOptions Options { get; }
protected EventErrorHandlerBase(IOptions<AbpEventBusOptions> options)
{
Options = options.Value;
}
public virtual async Task HandleAsync(EventExecutionErrorContext context)
{
if (!await ShouldHandleAsync(context))
{
ThrowOriginalExceptions(context);
}
if (await ShouldRetryAsync(context))
{
await RetryAsync(context);
return;
}
await MoveToDeadLetterAsync(context);
}
protected abstract Task RetryAsync(EventExecutionErrorContext context);
protected abstract Task MoveToDeadLetterAsync(EventExecutionErrorContext context);
protected virtual Task<bool> ShouldHandleAsync(EventExecutionErrorContext context)
{
if (!Options.EnabledErrorHandle)
{
return Task.FromResult(false);
}
return Task.FromResult(Options.ErrorHandleSelector == null || Options.ErrorHandleSelector.Invoke(context.EventType));
}
protected virtual Task<bool> ShouldRetryAsync(EventExecutionErrorContext context)
{
if (Options.RetryStrategyOptions == null)
{
return Task.FromResult(false);
}
if (!context.TryGetRetryAttempt(out var retryAttempt))
{
return Task.FromResult(false);
}
return Task.FromResult(Options.RetryStrategyOptions.MaxRetryAttempts > retryAttempt);
}
protected virtual void ThrowOriginalExceptions(EventExecutionErrorContext context)
{
if (context.Exceptions.Count == 1)
{
context.Exceptions[0].ReThrow();
}
throw new AggregateException(
"More than one error has occurred while triggering the event: " + context.EventType,
context.Exceptions);
}
}
}

38
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventExecutionErrorContext.cs

@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Data;
using Volo.Abp.ObjectExtending;
namespace Volo.Abp.EventBus
{
public class EventExecutionErrorContext : ExtensibleObject
{
public IReadOnlyList<Exception> Exceptions { get; }
public object EventData { get; set; }
public Type EventType { get; }
public IEventBus EventBus { get; }
public EventExecutionErrorContext(List<Exception> exceptions, Type eventType, IEventBus eventBus)
{
Exceptions = exceptions;
EventType = eventType;
EventBus = eventBus;
}
public bool TryGetRetryAttempt(out int retryAttempt)
{
retryAttempt = 0;
if (!this.HasProperty(EventErrorHandlerBase.RetryAttemptKey))
{
return false;
}
retryAttempt = this.GetProperty<int>(EventErrorHandlerBase.RetryAttemptKey);
return true;
}
}
}

9
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventErrorHandler.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.EventBus
{
public interface IEventErrorHandler
{
Task HandleAsync(EventExecutionErrorContext context);
}
}

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

@ -7,11 +7,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Json;
using Volo.Abp.Uow;
namespace Volo.Abp.EventBus.Local
@ -35,9 +33,8 @@ namespace Volo.Abp.EventBus.Local
IOptions<AbpLocalEventBusOptions> options,
IServiceScopeFactory serviceScopeFactory,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IEventErrorHandler errorHandler)
: base(serviceScopeFactory, currentTenant, unitOfWorkManager, errorHandler)
IUnitOfWorkManager unitOfWorkManager)
: base(serviceScopeFactory, currentTenant, unitOfWorkManager)
{
Options = options.Value;
Logger = NullLogger<LocalEventBus>.Instance;
@ -134,11 +131,7 @@ namespace Volo.Abp.EventBus.Local
public virtual async Task PublishAsync(LocalEventMessage localEventMessage)
{
await TriggerHandlersAsync(localEventMessage.EventType, localEventMessage.EventData, errorContext =>
{
errorContext.EventData = localEventMessage.EventData;
errorContext.SetProperty(nameof(LocalEventMessage.MessageId), localEventMessage.MessageId);
});
await TriggerHandlersAsync(localEventMessage.EventType, localEventMessage.EventData);
}
protected override IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType)

60
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventErrorHandler.cs

@ -1,60 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EventBus.Local
{
[ExposeServices(typeof(LocalEventErrorHandler), typeof(IEventErrorHandler))]
public class LocalEventErrorHandler : EventErrorHandlerBase, ISingletonDependency
{
protected Dictionary<Guid, int> RetryTracking { get; }
public LocalEventErrorHandler(
IOptions<AbpEventBusOptions> options)
: base(options)
{
RetryTracking = new Dictionary<Guid, int>();
}
protected override async Task RetryAsync(EventExecutionErrorContext context)
{
if (Options.RetryStrategyOptions.IntervalMillisecond > 0)
{
await Task.Delay(Options.RetryStrategyOptions.IntervalMillisecond);
}
var messageId = context.GetProperty<Guid>(nameof(LocalEventMessage.MessageId));
context.TryGetRetryAttempt(out var retryAttempt);
RetryTracking[messageId] = ++retryAttempt;
await context.EventBus.As<LocalEventBus>().PublishAsync(new LocalEventMessage(messageId, context.EventData, context.EventType));
RetryTracking.Remove(messageId);
}
protected override Task MoveToDeadLetterAsync(EventExecutionErrorContext context)
{
ThrowOriginalExceptions(context);
return Task.CompletedTask;
}
protected override async Task<bool> ShouldRetryAsync(EventExecutionErrorContext context)
{
var messageId = context.GetProperty<Guid>(nameof(LocalEventMessage.MessageId));
context.SetProperty(RetryAttemptKey, RetryTracking.GetOrDefault(messageId));
if (await base.ShouldRetryAsync(context))
{
return true;
}
RetryTracking.Remove(messageId);
return false;
}
}
}

2
framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/IKafkaMessageConsumerFactory.cs

@ -8,13 +8,11 @@
/// not disposed until end of the application.
/// </summary>
/// <param name="topicName"></param>
/// <param name="deadLetterTopicName"></param>
/// <param name="groupId"></param>
/// <param name="connectionName"></param>
/// <returns></returns>
IKafkaMessageConsumer Create(
string topicName,
string deadLetterTopicName,
string groupId,
string connectionName = null);
}

29
framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumer.cs

@ -39,8 +39,6 @@ namespace Volo.Abp.Kafka
protected string TopicName { get; private set; }
protected string DeadLetterTopicName { get; private set; }
public KafkaMessageConsumer(
IConsumerPool consumerPool,
IExceptionNotifier exceptionNotifier,
@ -64,15 +62,12 @@ namespace Volo.Abp.Kafka
public virtual void Initialize(
[NotNull] string topicName,
[NotNull] string deadLetterTopicName,
[NotNull] string groupId,
string connectionName = null)
{
Check.NotNull(topicName, nameof(topicName));
Check.NotNull(deadLetterTopicName, nameof(deadLetterTopicName));
Check.NotNull(groupId, nameof(groupId));
TopicName = topicName;
DeadLetterTopicName = deadLetterTopicName;
ConnectionName = connectionName ?? KafkaConnections.DefaultConnectionName;
GroupId = groupId;
Timer.Start();
@ -94,30 +89,18 @@ namespace Volo.Abp.Kafka
{
using (var adminClient = new AdminClientBuilder(Options.Connections.GetOrDefault(ConnectionName)).Build())
{
var topics = new List<TopicSpecification>
var topic = new TopicSpecification
{
new()
{
Name = TopicName,
NumPartitions = 1,
ReplicationFactor = 1
},
new()
{
Name = DeadLetterTopicName,
NumPartitions = 1,
ReplicationFactor = 1
}
Name = TopicName,
NumPartitions = 1,
ReplicationFactor = 1
};
topics.ForEach(topic =>
{
Options.ConfigureTopic?.Invoke(topic);
});
Options.ConfigureTopic?.Invoke(topic);
try
{
await adminClient.CreateTopicsAsync(topics);
await adminClient.CreateTopicsAsync(new[] {topic});
}
catch (CreateTopicsException e)
{

4
framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumerFactory.cs

@ -1,5 +1,4 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
@ -16,12 +15,11 @@ namespace Volo.Abp.Kafka
public IKafkaMessageConsumer Create(
string topicName,
string deadLetterTopicName,
string groupId,
string connectionName = null)
{
var consumer = ServiceScope.ServiceProvider.GetRequiredService<KafkaMessageConsumer>();
consumer.Initialize(topicName, deadLetterTopicName, groupId, connectionName);
consumer.Initialize(topicName, groupId, connectionName);
return consumer;
}

6
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ExchangeDeclareConfiguration.cs

@ -6,8 +6,6 @@ namespace Volo.Abp.RabbitMQ
{
public string ExchangeName { get; }
public string DeadLetterExchangeName { get; set; }
public string Type { get; }
public bool Durable { get; set; }
@ -20,11 +18,9 @@ namespace Volo.Abp.RabbitMQ
string exchangeName,
string type,
bool durable = false,
bool autoDelete = false,
string deadLetterExchangeName = null)
bool autoDelete = false)
{
ExchangeName = exchangeName;
DeadLetterExchangeName = deadLetterExchangeName;
Type = type;
Durable = durable;
AutoDelete = autoDelete;

6
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/QueueDeclareConfiguration.cs

@ -8,8 +8,6 @@ namespace Volo.Abp.RabbitMQ
{
[NotNull] public string QueueName { get; }
public string DeadLetterQueueName { get; set; }
public bool Durable { get; set; }
public bool Exclusive { get; set; }
@ -22,11 +20,9 @@ namespace Volo.Abp.RabbitMQ
[NotNull] string queueName,
bool durable = true,
bool exclusive = false,
bool autoDelete = false,
string deadLetterQueueName = null)
bool autoDelete = false)
{
QueueName = queueName;
DeadLetterQueueName = deadLetterQueueName;
Durable = durable;
Exclusive = exclusive;
AutoDelete = autoDelete;

38
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs

@ -157,29 +157,7 @@ namespace Volo.Abp.RabbitMQ
arguments: Exchange.Arguments
);
if (!Exchange.DeadLetterExchangeName.IsNullOrWhiteSpace() &&
!Queue.DeadLetterQueueName.IsNullOrWhiteSpace())
{
Channel.ExchangeDeclare(
Exchange.DeadLetterExchangeName,
Exchange.Type,
Exchange.Durable,
Exchange.AutoDelete
);
Channel.QueueDeclare(
Queue.DeadLetterQueueName,
Queue.Durable,
Queue.Exclusive,
Queue.AutoDelete);
Queue.Arguments["x-dead-letter-exchange"] = Exchange.DeadLetterExchangeName;
Queue.Arguments["x-dead-letter-routing-key"] = Queue.DeadLetterQueueName;
Channel.QueueBind(Queue.DeadLetterQueueName, Exchange.DeadLetterExchangeName, Queue.DeadLetterQueueName);
}
var result = Channel.QueueDeclare(
Channel.QueueDeclare(
queue: Queue.QueueName,
durable: Queue.Durable,
exclusive: Queue.Exclusive,
@ -202,11 +180,8 @@ namespace Volo.Abp.RabbitMQ
operationInterruptedException.ShutdownReason.ReplyCode == 406 &&
operationInterruptedException.Message.Contains("arg 'x-dead-letter-exchange'"))
{
Exchange.DeadLetterExchangeName = null;
Queue.DeadLetterQueueName = null;
Queue.Arguments.Remove("x-dead-letter-exchange");
Queue.Arguments.Remove("x-dead-letter-routing-key");
Logger.LogWarning("Unable to bind the dead letter queue to an existing queue. You can delete the queue or add policy. See: https://www.rabbitmq.com/parameters.html");
Logger.LogException(ex, LogLevel.Warning);
await ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning);
}
Logger.LogException(ex, LogLevel.Warning);
@ -229,8 +204,13 @@ namespace Volo.Abp.RabbitMQ
{
try
{
Channel.BasicReject(basicDeliverEventArgs.DeliveryTag, false);
Channel.BasicNack(
basicDeliverEventArgs.DeliveryTag,
multiple: false,
requeue: true
);
}
// ReSharper disable once EmptyGeneralCatchClause
catch { }
Logger.LogException(ex);

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

@ -5,17 +5,5 @@ namespace Volo.Abp.EventBus
[DependsOn(typeof(AbpEventBusModule))]
public class EventBusTestModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpEventBusOptions>(options =>
{
options.UseRetryStrategy(retryStrategyOptions =>
{
retryStrategyOptions.IntervalMillisecond = 0;
});
options.ErrorHandleSelector = type => type == typeof(MyExceptionHandleEventData);
});
}
}
}

72
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_Exception_Handler_Tests.cs

@ -1,72 +0,0 @@
using System;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.EventBus.Local
{
public class EventBus_Exception_Handler_Tests : EventBusTestBase
{
[Fact]
public async Task Should_Not_Handle_Exception()
{
var retryAttempt = 0;
LocalEventBus.Subscribe<MySimpleEventData>(eventData =>
{
retryAttempt++;
throw new Exception("This exception is intentionally thrown!");
});
var appException = await Assert.ThrowsAsync<Exception>(async () =>
{
await LocalEventBus.PublishAsync(new MySimpleEventData(1));
});
retryAttempt.ShouldBe(1);
appException.Message.ShouldBe("This exception is intentionally thrown!");
}
[Fact]
public async Task Should_Handle_Exception()
{
var retryAttempt = 0;
LocalEventBus.Subscribe<MyExceptionHandleEventData>(eventData =>
{
eventData.Value.ShouldBe(0);
retryAttempt++;
if (retryAttempt < 2)
{
throw new Exception("This exception is intentionally thrown!");
}
return Task.CompletedTask;
});
await LocalEventBus.PublishAsync(new MyExceptionHandleEventData(0));
retryAttempt.ShouldBe(2);
}
[Fact]
public async Task Should_Throw_Exception_After_Error_Handle()
{
var retryAttempt = 0;
LocalEventBus.Subscribe<MyExceptionHandleEventData>(eventData =>
{
eventData.Value.ShouldBe(0);
retryAttempt++;
throw new Exception("This exception is intentionally thrown!");
});
var appException = await Assert.ThrowsAsync<Exception>(async () =>
{
await LocalEventBus.PublishAsync(new MyExceptionHandleEventData(0));
});
retryAttempt.ShouldBe(4);
appException.Message.ShouldBe("This exception is intentionally thrown!");
}
}
}

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

@ -1,12 +0,0 @@
namespace Volo.Abp.EventBus
{
public class MyExceptionHandleEventData
{
public int Value { get; set; }
public MyExceptionHandleEventData(int value)
{
Value = value;
}
}
}

2
modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/AccountController.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.Account
[RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)]
[Area("account")]
[Route("api/account")]
public class AccountController : AbpController, IAccountAppService
public class AccountController : AbpControllerBase, IAccountAppService
{
protected IAccountAppService AccountAppService { get; }

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

@ -22,7 +22,7 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
[ControllerName("Login")]
[Area("account")]
[Route("api/account")]
public class AccountController : AbpController
public class AccountController : AbpControllerBase
{
protected SignInManager<IdentityUser> SignInManager { get; }
protected IdentityUserManager UserManager { get; }

2
modules/blogging/src/Volo.Blogging.Admin.HttpApi/Volo/Blogging/Admin/BlogManagementController.cs

@ -13,7 +13,7 @@ namespace Volo.Blogging.Admin
[RemoteService(Name = BloggingAdminRemoteServiceConsts.RemoteServiceName)]
[Area("bloggingAdmin")]
[Route("api/blogging/blogs/admin")]
public class BlogManagementController : AbpController, IBlogManagementAppService
public class BlogManagementController : AbpControllerBase, IBlogManagementAppService
{
private readonly IBlogManagementAppService _blogManagementAppService;

2
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs

@ -10,7 +10,7 @@ namespace Volo.Blogging
[RemoteService(Name = BloggingRemoteServiceConsts.RemoteServiceName)]
[Area("blogging")]
[Route("api/blogging/files")]
public class BlogFilesController : AbpController, IFileAppService
public class BlogFilesController : AbpControllerBase, IFileAppService
{
private readonly IFileAppService _fileAppService;

2
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs

@ -12,7 +12,7 @@ namespace Volo.Blogging
[RemoteService(Name = BloggingRemoteServiceConsts.RemoteServiceName)]
[Area("blogging")]
[Route("api/blogging/blogs")]
public class BlogsController : AbpController, IBlogAppService
public class BlogsController : AbpControllerBase, IBlogAppService
{
private readonly IBlogAppService _blogAppService;

2
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs

@ -12,7 +12,7 @@ namespace Volo.Blogging
[RemoteService(Name = BloggingRemoteServiceConsts.RemoteServiceName)]
[Area("blogging")]
[Route("api/blogging/comments")]
public class CommentsController : AbpController, ICommentAppService
public class CommentsController : AbpControllerBase, ICommentAppService
{
private readonly ICommentAppService _commentAppService;

2
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs

@ -11,7 +11,7 @@ namespace Volo.Blogging
[RemoteService(Name = BloggingRemoteServiceConsts.RemoteServiceName)]
[Area("blogging")]
[Route("api/blogging/posts")]
public class PostsController : AbpController, IPostAppService
public class PostsController : AbpControllerBase, IPostAppService
{
private readonly IPostAppService _postAppService;

2
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/TagsController.cs

@ -12,7 +12,7 @@ namespace Volo.Blogging
[RemoteService(Name = BloggingRemoteServiceConsts.RemoteServiceName)]
[Area("blogging")]
[Route("api/blogging/tags")]
public class TagsController : AbpController, ITagAppService
public class TagsController : AbpControllerBase, ITagAppService
{
private readonly ITagAppService _tagAppService;

2
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/CmsKitAdminController.cs

@ -3,7 +3,7 @@ using Volo.CmsKit.Localization;
namespace Volo.CmsKit.Admin
{
public abstract class CmsKitAdminController : AbpController
public abstract class CmsKitAdminController : AbpControllerBase
{
protected CmsKitAdminController()
{

2
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/CmsKitControllerBase.cs

@ -3,7 +3,7 @@ using Volo.CmsKit.Localization;
namespace Volo.CmsKit
{
public abstract class CmsKitControllerBase : AbpController
public abstract class CmsKitControllerBase : AbpControllerBase
{
protected CmsKitControllerBase()
{

2
modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs

@ -13,7 +13,7 @@ namespace Volo.Docs.Admin
[Area("docs-admin")]
[ControllerName("DocumentsAdmin")]
[Route("api/docs/admin/documents")]
public class DocumentsAdminController : AbpController, IDocumentAdminAppService
public class DocumentsAdminController : AbpControllerBase, IDocumentAdminAppService
{
private readonly IDocumentAdminAppService _documentAdminAppService;

2
modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/ProjectsAdminController.cs

@ -12,7 +12,7 @@ namespace Volo.Docs.Admin
[Area("docs-admin")]
[ControllerName("ProjectsAdmin")]
[Route("api/docs/admin/projects")]
public class ProjectsAdminController : AbpController, IProjectAdminAppService
public class ProjectsAdminController : AbpControllerBase, IProjectAdminAppService
{
private readonly IProjectAdminAppService _projectAppService;

2
modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Projects/DocsProjectController.cs

@ -11,7 +11,7 @@ namespace Volo.Docs.Projects
[Area("docs")]
[ControllerName("Project")]
[Route("api/docs/projects")]
public class DocsProjectController : AbpController, IProjectAppService
public class DocsProjectController : AbpControllerBase, IProjectAppService
{
protected IProjectAppService ProjectAppService { get; }

2
modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs

@ -7,7 +7,7 @@ namespace Volo.Abp.FeatureManagement
[RemoteService(Name = FeatureManagementRemoteServiceConsts.RemoteServiceName)]
[Area("featureManagement")]
[Route("api/feature-management/features")]
public class FeaturesController : AbpController, IFeatureAppService
public class FeaturesController : AbpControllerBase, IFeatureAppService
{
protected IFeatureAppService FeatureAppService { get; }

2
modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.Identity
[Area("identity")]
[ControllerName("Role")]
[Route("api/identity/roles")]
public class IdentityRoleController : AbpController, IIdentityRoleAppService
public class IdentityRoleController : AbpControllerBase, IIdentityRoleAppService
{
protected IIdentityRoleAppService RoleAppService { get; }

2
modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserController.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.Identity
[Area("identity")]
[ControllerName("User")]
[Route("api/identity/users")]
public class IdentityUserController : AbpController, IIdentityUserAppService
public class IdentityUserController : AbpControllerBase, IIdentityUserAppService
{
protected IIdentityUserAppService UserAppService { get; }

2
modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs

@ -11,7 +11,7 @@ namespace Volo.Abp.Identity
[Area("identity")]
[ControllerName("UserLookup")]
[Route("api/identity/users/lookup")]
public class IdentityUserLookupController : AbpController, IIdentityUserLookupAppService
public class IdentityUserLookupController : AbpControllerBase, IIdentityUserLookupAppService
{
protected IIdentityUserLookupAppService LookupAppService { get; }

2
modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/ProfileController.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.Identity
[Area("identity")]
[ControllerName("Profile")]
[Route("/api/identity/my-profile")]
public class ProfileController : AbpController, IProfileAppService
public class ProfileController : AbpControllerBase, IProfileAppService
{
protected IProfileAppService ProfileAppService { get; }

2
modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs

@ -7,7 +7,7 @@ namespace Volo.Abp.PermissionManagement
[RemoteService(Name = PermissionManagementRemoteServiceConsts.RemoteServiceName)]
[Area("permissionManagement")]
[Route("api/permission-management/permissions")]
public class PermissionsController : AbpController, IPermissionAppService
public class PermissionsController : AbpControllerBase, IPermissionAppService
{
protected IPermissionAppService PermissionAppService { get; }

2
modules/setting-management/src/Volo.Abp.SettingManagement.HttpApi/Volo/Abp/SettingManagement/EmailSettingsController.cs

@ -7,7 +7,7 @@ namespace Volo.Abp.SettingManagement
[RemoteService(Name = SettingManagementRemoteServiceConsts.RemoteServiceName)]
[Area("settingManagement")]
[Route("api/setting-management/emailing")]
public class EmailSettingsController : AbpController, IEmailSettingsAppService
public class EmailSettingsController : AbpControllerBase, IEmailSettingsAppService
{
private readonly IEmailSettingsAppService _emailSettingsAppService;

2
modules/tenant-management/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.TenantManagement
[RemoteService(Name = TenantManagementRemoteServiceConsts.RemoteServiceName)]
[Area("multi-tenancy")]
[Route("api/multi-tenancy/tenants")]
public class TenantController : AbpController, ITenantAppService //TODO: Throws exception on validation if we inherit from Controller
public class TenantController : AbpControllerBase, ITenantAppService //TODO: Throws exception on validation if we inherit from Controller
{
protected ITenantAppService TenantAppService { get; }

2
npm/ng-packs/packages/components/chart.js/src/chart.component.ts

@ -88,7 +88,7 @@ export class ChartComponent implements AfterViewInit, OnDestroy, OnChanges {
}
}
initChart = () => {
private initChart = () => {
const opts = this.options || {};
opts.responsive = this.responsive;

26
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html

@ -25,14 +25,24 @@
>
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template>
<ng-container *abpPermission="prop.permission">
<div
*ngIf="row['_' + prop.name]?.visible"
[innerHTML]="row['_' + prop.name]?.value | async"
(click)="
prop.action && prop.action({ getInjected: getInjected, record: row, index: i })
"
[class.pointer]="prop.action"
></div>
<ng-container *ngIf="row['_' + prop.name]?.visible">
<div
*ngIf="!row['_' + prop.name].component; else component"
[innerHTML]="row['_' + prop.name]?.value | async"
(click)="
prop.action && prop.action({ getInjected: getInjected, record: row, index: i })
"
[class.pointer]="prop.action"
></div>
</ng-container>
<ng-template #component>
<ng-container
*ngComponentOutlet="
row['_' + prop.name].component;
injector: row['_' + prop.name].injector
"
></ng-container>
</ng-template>
</ng-container>
</ng-template>
</ngx-datatable-column>

34
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts

@ -1,4 +1,5 @@
import {
ABP,
ConfigStateService,
getShortDateFormat,
getShortDateShortTimeFormat,
@ -29,7 +30,8 @@ import { EntityActionList } from '../../models/entity-actions';
import { EntityProp, EntityPropList } from '../../models/entity-props';
import { PropData } from '../../models/props';
import { ExtensionsService } from '../../services/extensions.service';
import { EXTENSIONS_IDENTIFIER } from '../../tokens/extensions.token';
import { EXTENSIONS_IDENTIFIER, PROP_DATA_STREAM } from '../../tokens/extensions.token';
const DEFAULT_ACTIONS_COLUMN_WIDTH = 150;
@Component({
@ -71,7 +73,7 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
constructor(
@Inject(LOCALE_ID) private locale: string,
private config: ConfigStateService,
injector: Injector,
private injector: Injector,
) {
this.getInjected = injector.get.bind(injector);
const extensions = injector.get(ExtensionsService);
@ -106,6 +108,12 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
: '<div class="text-center text-danger"><i class="fa fa-times"></i></div>';
}
private getEnum(rowValue: any, list: Array<ABP.Option<any>>) {
if (!list) return rowValue;
const { key } = list.find(({ value }) => value === rowValue);
return key;
}
getContent(prop: EntityProp<R>, data: PropData): Observable<string> {
return prop.valueResolver(data).pipe(
map(value => {
@ -118,6 +126,8 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
return this.getDate(value, getShortTimeFormat(this.config));
case ePropType.DateTime:
return this.getDate(value, getShortDateShortTimeFormat(this.config));
case ePropType.Enum:
return this.getEnum(value, prop.enumList);
default:
return value;
// More types can be handled in the future
@ -132,10 +142,26 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
this.data = data.currentValue.map((record, index) => {
this.propList.forEach(prop => {
const propData = { getInjected: this.getInjected, record, index } as any;
record[`_${prop.value.name}`] = {
const value = this.getContent(prop.value, propData);
const propKey = `_${prop.value.name}`;
record[propKey] = {
visible: prop.value.visible(propData),
value: this.getContent(prop.value, propData),
value,
};
if (prop.value.component) {
const injector = Injector.create(
[
{
provide: PROP_DATA_STREAM,
useValue: value,
},
],
this.injector,
);
record[propKey].injector = injector;
record[propKey].component = prop.value.component;
}
});
return record;

7
npm/ng-packs/packages/theme-shared/extensions/src/lib/models/entity-props.ts

@ -1,6 +1,7 @@
import { Type } from '@angular/core';
import { Observable, of } from 'rxjs';
import { O } from 'ts-toolbelt';
import { ABP } from '@abp/ng.core';
import { ActionCallback } from './actions';
import {
Prop,
@ -27,6 +28,8 @@ export class EntityProp<R = any> extends Prop<R> {
readonly sortable: boolean;
readonly valueResolver: PropCallback<R, Observable<any>>;
readonly action: ActionCallback<R>;
readonly component: Type<any>;
readonly enumList: Array<ABP.Option<any>>;
constructor(options: EntityPropOptions<R>) {
super(
@ -42,6 +45,8 @@ export class EntityProp<R = any> extends Prop<R> {
this.sortable = options.sortable || false;
this.valueResolver = options.valueResolver || (data => of(data.record[this.name]));
this.action = options.action;
this.component = options.component;
this.enumList = options.enumList;
}
static create<R = any>(options: EntityPropOptions<R>) {
@ -63,6 +68,8 @@ export type EntityPropOptions<R = any> = O.Optional<
| 'sortable'
| 'valueResolver'
| 'action'
| 'component'
| 'enumList'
>;
export type EntityPropDefaults<R = any> = Record<string, EntityProp<R>[]>;

2
npm/ng-packs/packages/theme-shared/extensions/src/lib/tokens/extensions.token.ts

@ -1,6 +1,7 @@
import { InjectionToken } from '@angular/core';
import { ActionCallback, ReadonlyActionData as ActionData } from '../models/actions';
import { ExtensionsService } from '../services/extensions.service';
import { Observable } from 'rxjs';
export const EXTENSIONS_IDENTIFIER = new InjectionToken<string>('EXTENSIONS_IDENTIFIER');
export type ActionKeys = Extract<'entityActions' | 'toolbarActions', keyof ExtensionsService>;
@ -11,3 +12,4 @@ export const EXTENSIONS_ACTION_DATA = new InjectionToken<ActionData>('EXTENSIONS
export const EXTENSIONS_ACTION_CALLBACK = new InjectionToken<ActionCallback<unknown>>(
'EXTENSIONS_ACTION_DATA',
);
export const PROP_DATA_STREAM = new InjectionToken<Observable<any>>('PROP_DATA_STREAM');

23
npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb-items/breadcrumb-items.component.html

@ -0,0 +1,23 @@
<ol class="breadcrumb" *ngIf="items.length">
<li class="breadcrumb-item">
<a routerLink="/"><i class="fa fa-home"></i> </a>
</li>
<li
*ngFor="let item of items; let last = last"
class="breadcrumb-item"
[class.active]="last"
aria-current="page"
>
<ng-container
*ngTemplateOutlet="item.path ? linkTemplate : textTemplate; context: { $implicit: item }"
></ng-container>
</li>
</ol>
<ng-template #linkTemplate let-item>
<a [routerLink]="item.path"> {{ item.name | abpLocalization }}</a>
</ng-template>
<ng-template #textTemplate let-item>
{{ item.name | abpLocalization }}
</ng-template>

9
npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb-items/breadcrumb-items.component.ts

@ -0,0 +1,9 @@
import { Component, Input } from '@angular/core';
@Component({
selector: 'abp-breadcrumb-items',
templateUrl: './breadcrumb-items.component.html',
})
export class BreadcrumbItemsComponent {
@Input() items = [];
}

14
npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html

@ -1,13 +1 @@
<ol class="breadcrumb" *ngIf="segments.length">
<li class="breadcrumb-item">
<a routerLink="/"><i class="fa fa-home"></i> </a>
</li>
<li
*ngFor="let segment of segments; let last = last"
class="breadcrumb-item"
[class.active]="last"
aria-current="page"
>
{{ segment.name | abpLocalization }}
</li>
</ol>
<abp-breadcrumb-items [items]="segments"></abp-breadcrumb-items>

13
npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts

@ -1,11 +1,4 @@
import {
ABP,
getRoutePath,
RouterEvents,
RoutesService,
SubscriptionService,
TreeNode,
} from '@abp/ng.core';
import { ABP, getRoutePath, RouterEvents, RoutesService, SubscriptionService, TreeNode } from '@abp/ng.core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { map, startWith } from 'rxjs/operators';
@ -41,7 +34,7 @@ export class BreadcrumbComponent implements OnInit {
while (node.parent) {
node = node.parent;
const { parent, children, isLeaf, ...segment } = node;
const { parent, children, isLeaf, path, ...segment } = node;
if (!isAdministration(segment)) this.segments.unshift(segment);
}
@ -52,6 +45,6 @@ export class BreadcrumbComponent implements OnInit {
}
}
function isAdministration(route: ABP.Route) {
function isAdministration(route: Pick<ABP.Route, 'name'>) {
return route.name === eThemeSharedRouteNames.Administration;
}

2
npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

@ -11,6 +11,7 @@ import {
} from '@ngx-validate/core';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';
import { BreadcrumbItemsComponent } from './components/breadcrumb-items/breadcrumb-items.component';
import { ButtonComponent } from './components/button/button.component';
import { ConfirmationComponent } from './components/confirmation/confirmation.component';
import { HttpErrorWrapperComponent } from './components/http-error-wrapper/http-error-wrapper.component';
@ -36,6 +37,7 @@ import { DateParserFormatter } from './utils/date-parser-formatter';
const declarationsWithExports = [
BreadcrumbComponent,
BreadcrumbItemsComponent,
ButtonComponent,
ConfirmationComponent,
LoaderBarComponent,

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/Controllers/MyProjectNameController.cs

@ -5,7 +5,7 @@ namespace MyCompanyName.MyProjectName.Controllers
{
/* Inherit your controllers from this class.
*/
public abstract class MyProjectNameController : AbpController
public abstract class MyProjectNameController : AbpControllerBase
{
protected MyProjectNameController()
{

2
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyProjectNameController.cs

@ -3,7 +3,7 @@ using Volo.Abp.AspNetCore.Mvc;
namespace MyCompanyName.MyProjectName
{
public abstract class MyProjectNameController : AbpController
public abstract class MyProjectNameController : AbpControllerBase
{
protected MyProjectNameController()
{

Loading…
Cancel
Save