From fef1eb61ebc4e19f6818e213091cf98ad92d4962 Mon Sep 17 00:00:00 2001
From: cKey <35512826+colinin@users.noreply.github.com>
Date: Fri, 25 Mar 2022 18:23:02 +0800
Subject: [PATCH 01/17] feat(webhooks): added support webhooks.
---
aspnet-core/LINGYUN.MicroService.All.sln | 10 +
aspnet-core/LINGYUN.MicroService.Common.sln | 10 +
.../LINGYUN.Abp.WebHooks/FodyWeavers.xml | 3 +
.../LINGYUN.Abp.WebHooks/FodyWeavers.xsd | 30 ++
.../LINGYUN.Abp.WebHooks.csproj | 18 +
.../LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs | 18 +
.../Abp/Webhooks/AbpWebhooksOptions.cs | 26 ++
.../BackgroundWorker/WebhookSenderJob.cs | 125 +++++++
.../Abp/Webhooks/DefaultWebhookPublisher.cs | 125 +++++++
.../Abp/Webhooks/DefaultWebhookSender.cs | 133 ++++++++
.../Abp/Webhooks/IWebhookDefinitionContext.cs | 22 ++
.../Abp/Webhooks/IWebhookDefinitionManager.cs | 31 ++
.../Abp/Webhooks/IWebhookEventStore.cs | 18 +
.../LINGYUN/Abp/Webhooks/IWebhookManager.cs | 22 ++
.../LINGYUN/Abp/Webhooks/IWebhookPublisher.cs | 56 ++++
.../Abp/Webhooks/IWebhookSendAttemptStore.cs | 25 ++
.../LINGYUN/Abp/Webhooks/IWebhookSender.cs | 16 +
.../Webhooks/IWebhookSubscriptionManager.cs | 74 +++++
.../Webhooks/IWebhookSubscriptionsStore.cs | 83 +++++
.../Abp/Webhooks/NullWebhookEventStore.cs | 24 ++
.../Webhooks/NullWebhookSendAttemptStore.cs | 47 +++
.../Webhooks/NullWebhookSubscriptionsStore.cs | 65 ++++
.../LINGYUN/Abp/Webhooks/WebhookDefinition.cs | 57 ++++
.../Abp/Webhooks/WebhookDefinitionContext.cs | 54 +++
.../Abp/Webhooks/WebhookDefinitionManager.cs | 109 +++++++
.../Abp/Webhooks/WebhookDefinitionProvider.cs | 13 +
.../LINGYUN/Abp/Webhooks/WebhookEvent.cs | 30 ++
.../LINGYUN/Abp/Webhooks/WebhookHeader.cs | 18 +
.../LINGYUN/Abp/Webhooks/WebhookManager.cs | 84 +++++
.../LINGYUN/Abp/Webhooks/WebhookPayload.cs | 35 ++
.../Abp/Webhooks/WebhookSendAttempt.cs | 39 +++
.../LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs | 67 ++++
.../Abp/Webhooks/WebhookSubscriptionInfo.cs | 60 ++++
.../Webhooks/WebhookSubscriptionManager.cs | 175 ++++++++++
.../System/AbpStringCryptographyExtensions.cs | 13 +
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
...oksManagement.Application.Contracts.csproj | 21 ++
...sManagementPermissionDefinitionProvider.cs | 22 ++
.../WebhooksManagementPermissions.cs | 8 +
...ooksManagementFeatureDefinitionProvider.cs | 18 +
.../WebhooksManagementFeatureNames.cs | 6 +
...oksManagementApplicationContractsModule.cs | 15 +
.../WebhooksManagementRemoteServiceConsts.cs | 7 +
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
....Abp.WebhooksManagement.Application.csproj | 20 ++
.../WebhooksManagementAppServiceBase.cs | 13 +
...hooksManagementApplicationMapperProfile.cs | 10 +
.../WebhooksManagementApplicationModule.cs | 24 ++
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
....Abp.WebhooksManagement.Dapr.Client.csproj | 16 +
.../WebhooksManagementDaprClientModule.cs | 18 +
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
...bp.WebhooksManagement.Domain.Shared.csproj | 25 ++
.../Localization/Resources/en.json | 8 +
.../Localization/Resources/zh-Hans.json | 8 +
.../WebhooksManagementResource.cs | 8 +
...sManagementModuleExtensionConfiguration.cs | 16 +
...ensionConfigurationDictionaryExtensions.cs | 19 ++
...WebhooksManagementModuleExtensionConsts.cs | 11 +
.../WebhooksManagementDomainSharedModule.cs | 32 ++
.../WebhooksManagementErrorCodes.cs | 6 +
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
...NGYUN.Abp.WebhooksManagement.Domain.csproj | 22 ++
.../DefaultWebhookManager.cs | 70 ++++
.../IWebhookEventRecordRepository.cs | 8 +
.../IWebhookSendRecordRepository.cs | 21 ++
.../IWebhookSubscriptionRepository.cs | 8 +
...ooksManagementSettingDefinitionProvider.cs | 11 +
.../Settings/WebhooksManagementSettings.cs | 7 +
.../WebhooksManagement/WebhookEventRecord.cs | 30 ++
.../WebhooksManagement/WebhookEventStore.cs | 57 ++++
.../WebhookSendAttemptStore.cs | 165 ++++++++++
.../WebhooksManagement/WebhookSendRecord.cs | 50 +++
.../WebhookSendRecordFilter.cs | 21 ++
.../WebhooksManagement/WebhookSubscription.cs | 33 ++
.../WebhookSubscriptionsStore.cs | 55 ++++
.../WebhooksManagementDbProperties.cs | 11 +
.../WebhooksManagementDomainMapperProfile.cs | 11 +
.../WebhooksManagementDomainModule.cs | 43 +++
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
...hooksManagement.EntityFrameworkCore.csproj | 19 ++
.../IWebhooksManagementDbContext.cs | 9 +
.../WebhooksManagementDbContext.cs | 21 ++
...agementDbContextModelCreatingExtensions.cs | 21 ++
...ooksManagementEfCoreQueryableExtensions.cs | 6 +
...ooksManagementEntityFrameworkCoreModule.cs | 19 ++
...agementModelBuilderConfigurationOptions.cs | 17 +
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
...p.WebhooksManagement.HttpApi.Client.csproj | 19 ++
.../WebhooksManagementHttpApiClientModule.cs | 18 +
.../FodyWeavers.xml | 3 +
.../FodyWeavers.xsd | 30 ++
...GYUN.Abp.WebhooksManagement.HttpApi.csproj | 19 ++
.../WebhooksManagementControllerBase.cs | 12 +
.../WebhooksManagementHttpApiModule.cs | 40 +++
.../Dockerfile | 4 +
...Service.TaskManagement.HttpApi.Host.csproj | 2 +
.../Controllers/HomeController.cs | 12 +
.../WebhooksManagementDataSeederWorker.cs | 21 ++
.../Dockerfile | 15 +
.../WebhooksManagementMigrationsDbContext.cs | 20 ++
...oksManagementMigrationsDbContextFactory.cs | 32 ++
.../EventBus/Handlers/TenantSynchronizer.cs | 69 ++++
...ice.WebhooksManagement.HttpApi.Host.csproj | 66 ++++
.../Program.cs | 58 ++++
.../Properties/launchSettings.json | 28 ++
.../TenantHeaderParamter.cs | 31 ++
...ksManagementHttpApiHostModule.Configure.cs | 307 ++++++++++++++++++
...sManagementHttpApiHostModule.DataSeeder.cs | 15 +
.../WebhooksManagementHttpApiHostModule.cs | 118 +++++++
.../appsettings.Development.json | 132 ++++++++
.../appsettings.json | 80 +++++
.../dapr.sh | 1 +
120 files changed, 4184 insertions(+)
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json
create mode 100644 aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh
diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln
index 2d20d15c8..3bda2e147 100644
--- a/aspnet-core/LINGYUN.MicroService.All.sln
+++ b/aspnet-core/LINGYUN.MicroService.All.sln
@@ -394,6 +394,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Saas.HttpApi.Cl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.Saas", "modules\tenants\LINGYUN.Abp.MultiTenancy.Saas\LINGYUN.Abp.MultiTenancy.Saas.csproj", "{F57594AA-10C2-4DFF-87F6-19F2548099EA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "webhooks", "webhooks", "{13ACF670-F109-404E-B252-2FA34A4EA061}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WebHooks", "modules\webhooks\LINGYUN.Abp.WebHooks\LINGYUN.Abp.WebHooks.csproj", "{91AE01B1-CC82-40E2-8290-B8A84C6E90D1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1020,6 +1024,10 @@ Global
{F57594AA-10C2-4DFF-87F6-19F2548099EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F57594AA-10C2-4DFF-87F6-19F2548099EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F57594AA-10C2-4DFF-87F6-19F2548099EA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {91AE01B1-CC82-40E2-8290-B8A84C6E90D1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1212,6 +1220,8 @@ Global
{8DF50094-6791-4C7C-B07D-C3E995B69C49} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{96EF6CDD-CD29-4E7B-B86A-3EBEE6AC9FDC} = {D01D859E-4B72-478A-BABD-90F0981652D5}
{F57594AA-10C2-4DFF-87F6-19F2548099EA} = {A5543E56-DA53-494D-A531-DA75091D46FF}
+ {13ACF670-F109-404E-B252-2FA34A4EA061} = {C5CAD011-DF84-4914-939C-0C029DCEF26F}
+ {91AE01B1-CC82-40E2-8290-B8A84C6E90D1} = {13ACF670-F109-404E-B252-2FA34A4EA061}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}
diff --git a/aspnet-core/LINGYUN.MicroService.Common.sln b/aspnet-core/LINGYUN.MicroService.Common.sln
index e2fdcc2f0..edeb3b22c 100644
--- a/aspnet-core/LINGYUN.MicroService.Common.sln
+++ b/aspnet-core/LINGYUN.MicroService.Common.sln
@@ -226,6 +226,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dapr.Actors.Asp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.MultiTenancy.Editions", "modules\tenants\LINGYUN.Abp.MultiTenancy.Editions\LINGYUN.Abp.MultiTenancy.Editions.csproj", "{3FF4CEA0-1555-4D62-AA81-B3B599253F8D}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "webhooks", "webhooks", "{BD97C98B-0B4B-443D-AB29-145A344F46D3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WebHooks", "modules\webhooks\LINGYUN.Abp.WebHooks\LINGYUN.Abp.WebHooks.csproj", "{AFE75D2B-8853-488B-B5D5-277B58C5DBB2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -580,6 +584,10 @@ Global
{3FF4CEA0-1555-4D62-AA81-B3B599253F8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FF4CEA0-1555-4D62-AA81-B3B599253F8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FF4CEA0-1555-4D62-AA81-B3B599253F8D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AFE75D2B-8853-488B-B5D5-277B58C5DBB2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -692,6 +700,8 @@ Global
{FF518E10-C9AB-440C-8E8D-9CFF67A926AC} = {3A0784A6-AFBF-406F-B79E-9505EB100445}
{49E0B90B-8635-43D0-B0AB-9D484CAE68B5} = {7FDFB22F-1BFF-4E05-9427-78B7A8461D50}
{3FF4CEA0-1555-4D62-AA81-B3B599253F8D} = {38E21687-5F19-42C9-9D11-4B1D2EF64EDB}
+ {BD97C98B-0B4B-443D-AB29-145A344F46D3} = {02EA4E78-5891-43BC-944F-3E52FEE032E4}
+ {AFE75D2B-8853-488B-B5D5-277B58C5DBB2} = {BD97C98B-0B4B-443D-AB29-145A344F46D3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml
new file mode 100644
index 000000000..17d32672d
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj
new file mode 100644
index 000000000..ca6031c7a
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs
new file mode 100644
index 000000000..03af8e4ef
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs
@@ -0,0 +1,18 @@
+using Volo.Abp.BackgroundJobs;
+using Volo.Abp.Features;
+using Volo.Abp.Guids;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Webhooks;
+
+[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))]
+[DependsOn(typeof(AbpFeaturesModule))]
+[DependsOn(typeof(AbpGuidsModule))]
+[DependsOn(typeof(AbpHttpClientModule))]
+public class AbpWebhooksModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs
new file mode 100644
index 000000000..7ac2ec457
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs
@@ -0,0 +1,26 @@
+using System;
+using Volo.Abp.Collections;
+
+namespace LINGYUN.Abp.Webhooks;
+
+public class AbpWebhooksOptions
+{
+ public TimeSpan TimeoutDuration { get; set; }
+
+ public int MaxSendAttemptCount { get; set; }
+
+ public bool IsAutomaticSubscriptionDeactivationEnabled { get; set; }
+
+ public int MaxConsecutiveFailCountBeforeDeactivateSubscription { get; set; }
+
+ public ITypeList DefinitionProviders { get; }
+
+ public AbpWebhooksOptions()
+ {
+ TimeoutDuration = TimeSpan.FromSeconds(60);
+ MaxSendAttemptCount = 5;
+ MaxConsecutiveFailCountBeforeDeactivateSubscription = MaxSendAttemptCount * 3;
+
+ DefinitionProviders = new TypeList();
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs
new file mode 100644
index 000000000..4d18cb209
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs
@@ -0,0 +1,125 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.BackgroundJobs;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.Webhooks.BackgroundWorker
+{
+ public class WebhookSenderJob : AsyncBackgroundJob, ITransientDependency
+ {
+ private readonly IUnitOfWorkManager _unitOfWorkManager;
+ private readonly IWebhookSubscriptionManager _webhookSubscriptionManager;
+ private readonly IWebhookSendAttemptStore _webhookSendAttemptStore;
+ private readonly IWebhookSender _webhookSender;
+
+ private readonly AbpWebhooksOptions _options;
+
+ public WebhookSenderJob(
+ IUnitOfWorkManager unitOfWorkManager,
+ IWebhookSubscriptionManager webhookSubscriptionManager,
+ IWebhookSendAttemptStore webhookSendAttemptStore,
+ IWebhookSender webhookSender,
+ IOptions options)
+ {
+ _unitOfWorkManager = unitOfWorkManager;
+ _webhookSubscriptionManager = webhookSubscriptionManager;
+ _webhookSendAttemptStore = webhookSendAttemptStore;
+ _webhookSender = webhookSender;
+ _options = options.Value;
+ }
+
+ public override async Task ExecuteAsync(WebhookSenderArgs args)
+ {
+ if (args.TryOnce)
+ {
+ try
+ {
+ await SendWebhook(args);
+ }
+ catch (Exception e)
+ {
+ Logger.LogWarning("An error occured while sending webhook with try once.", e);
+ // ignored
+ }
+ }
+ else
+ {
+ await SendWebhook(args);
+ }
+ }
+
+ private async Task SendWebhook(WebhookSenderArgs args)
+ {
+ if (args.WebhookEventId == default)
+ {
+ return;
+ }
+
+ if (args.WebhookSubscriptionId == default)
+ {
+ return;
+ }
+
+ if (!args.TryOnce)
+ {
+ var sendAttemptCount = await _webhookSendAttemptStore.GetSendAttemptCountAsync(
+ args.TenantId,
+ args.WebhookEventId,
+ args.WebhookSubscriptionId
+ );
+
+ if (sendAttemptCount > _options.MaxSendAttemptCount)
+ {
+ return;
+ }
+ }
+
+ try
+ {
+ await _webhookSender.SendWebhookAsync(args);
+ }
+ catch (Exception)
+ {
+ // no need to retry to send webhook since subscription disabled
+ if (!await TryDeactivateSubscriptionIfReachedMaxConsecutiveFailCount(
+ args.TenantId,
+ args.WebhookSubscriptionId))
+ {
+ throw; //Throw exception to re-try sending webhook
+ }
+ }
+ }
+
+ private async Task TryDeactivateSubscriptionIfReachedMaxConsecutiveFailCount(
+ Guid? tenantId,
+ Guid subscriptionId)
+ {
+ if (!_options.IsAutomaticSubscriptionDeactivationEnabled)
+ {
+ return false;
+ }
+
+ var hasXConsecutiveFail = await _webhookSendAttemptStore
+ .HasXConsecutiveFailAsync(
+ tenantId,
+ subscriptionId,
+ _options.MaxConsecutiveFailCountBeforeDeactivateSubscription
+ );
+
+ if (!hasXConsecutiveFail)
+ {
+ return false;
+ }
+
+ using (var uow = _unitOfWorkManager.Begin())
+ {
+ await _webhookSubscriptionManager.ActivateWebhookSubscriptionAsync(subscriptionId, false);
+ await uow.CompleteAsync();
+ return true;
+ }
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
new file mode 100644
index 000000000..d466ee0f8
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp.BackgroundJobs;
+using Volo.Abp.Guids;
+using Volo.Abp.Json;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class DefaultWebhookPublisher : IWebhookPublisher
+ {
+ public IWebhookEventStore WebhookEventStore { get; set; }
+
+ private readonly ICurrentTenant _currentTenant;
+ private readonly IGuidGenerator _guidGenerator;
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IBackgroundJobManager _backgroundJobManager;
+ private readonly IWebhookSubscriptionManager _webhookSubscriptionManager;
+
+ public DefaultWebhookPublisher(
+ IWebhookSubscriptionManager webhookSubscriptionManager,
+ ICurrentTenant currentTenant,
+ IGuidGenerator guidGenerator,
+ IJsonSerializer jsonSerializer,
+ IBackgroundJobManager backgroundJobManager)
+ {
+ _currentTenant = currentTenant;
+ _guidGenerator = guidGenerator;
+ _jsonSerializer = jsonSerializer;
+ _backgroundJobManager = backgroundJobManager;
+ _webhookSubscriptionManager = webhookSubscriptionManager;
+
+ WebhookEventStore = NullWebhookEventStore.Instance;
+ }
+
+ #region Async Publish Methods
+ public virtual async Task PublishAsync(string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null)
+ {
+ var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsIfFeaturesGrantedAsync(_currentTenant.Id, webhookName);
+ await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers);
+ }
+
+ public virtual async Task PublishAsync(string webhookName, object data, Guid? tenantId,
+ bool sendExactSameData = false, WebhookHeader headers = null)
+ {
+ var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsIfFeaturesGrantedAsync(tenantId, webhookName);
+ await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers);
+ }
+
+ public virtual async Task PublishAsync(Guid?[] tenantIds, string webhookName, object data,
+ bool sendExactSameData = false, WebhookHeader headers = null)
+ {
+ var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(tenantIds, webhookName);
+ await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers);
+ }
+
+ private async Task PublishAsync(string webhookName, object data, List webhookSubscriptions,
+ bool sendExactSameData = false, WebhookHeader headers = null)
+ {
+ if (webhookSubscriptions.IsNullOrEmpty())
+ {
+ return;
+ }
+
+ var subscriptionsGroupedByTenant = webhookSubscriptions.GroupBy(x => x.TenantId);
+
+ foreach (var subscriptionGroupedByTenant in subscriptionsGroupedByTenant)
+ {
+ var webhookInfo = await SaveAndGetWebhookAsync(subscriptionGroupedByTenant.Key, webhookName, data);
+
+ foreach (var webhookSubscription in subscriptionGroupedByTenant)
+ {
+ var headersToSend = webhookSubscription.Headers;
+ if (headers != null)
+ {
+ if (headers.UseOnlyGivenHeaders)//do not use the headers defined in subscription
+ {
+ headersToSend = headers.Headers;
+ }
+ else
+ {
+ //use the headers defined in subscription. If additional headers has same header, use additional headers value.
+ foreach (var additionalHeader in headers.Headers)
+ {
+ headersToSend[additionalHeader.Key] = additionalHeader.Value;
+ }
+ }
+ }
+
+ await _backgroundJobManager.EnqueueAsync(new WebhookSenderArgs
+ {
+ TenantId = webhookSubscription.TenantId,
+ WebhookEventId = webhookInfo.Id,
+ Data = webhookInfo.Data,
+ WebhookName = webhookInfo.WebhookName,
+ WebhookSubscriptionId = webhookSubscription.Id,
+ Headers = headersToSend,
+ Secret = webhookSubscription.Secret,
+ WebhookUri = webhookSubscription.WebhookUri,
+ SendExactSameData = sendExactSameData
+ });
+ }
+ }
+ }
+
+ #endregion
+
+ protected virtual async Task SaveAndGetWebhookAsync(Guid? tenantId, string webhookName,
+ object data)
+ {
+ var webhookInfo = new WebhookEvent
+ {
+ Id = _guidGenerator.Create(),
+ WebhookName = webhookName,
+ Data = _jsonSerializer.Serialize(data),
+ TenantId = tenantId
+ };
+
+ await WebhookEventStore.InsertAndGetIdAsync(webhookInfo);
+ return webhookInfo;
+ }
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs
new file mode 100644
index 000000000..2dc939dc7
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs
@@ -0,0 +1,133 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
+using System;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class DefaultWebhookSender : IWebhookSender
+ {
+ public ILogger Logger { protected get; set; }
+
+ private readonly AbpWebhooksOptions _options;
+ private readonly IWebhookManager _webhookManager;
+
+ private const string FailedRequestDefaultContent = "Webhook Send Request Failed";
+
+ public DefaultWebhookSender(
+ IOptions options,
+ IWebhookManager webhookManager)
+ {
+ _options = options.Value;
+ _webhookManager = webhookManager;
+
+ Logger = NullLogger.Instance;
+ }
+
+ public async Task SendWebhookAsync(WebhookSenderArgs webhookSenderArgs)
+ {
+ if (webhookSenderArgs.WebhookEventId == default)
+ {
+ throw new ArgumentNullException(nameof(webhookSenderArgs.WebhookEventId));
+ }
+
+ if (webhookSenderArgs.WebhookSubscriptionId == default)
+ {
+ throw new ArgumentNullException(nameof(webhookSenderArgs.WebhookSubscriptionId));
+ }
+
+ var webhookSendAttemptId = await _webhookManager.InsertAndGetIdWebhookSendAttemptAsync(webhookSenderArgs);
+
+ var request = CreateWebhookRequestMessage(webhookSenderArgs);
+
+ var serializedBody = await _webhookManager.GetSerializedBodyAsync(webhookSenderArgs);
+
+ _webhookManager.SignWebhookRequest(request, serializedBody, webhookSenderArgs.Secret);
+
+ AddAdditionalHeaders(request, webhookSenderArgs);
+
+ var isSucceed = false;
+ HttpStatusCode? statusCode = null;
+ var content = FailedRequestDefaultContent;
+
+ try
+ {
+ var response = await SendHttpRequest(request);
+ isSucceed = response.isSucceed;
+ statusCode = response.statusCode;
+ content = response.content;
+ }
+ catch (TaskCanceledException)
+ {
+ statusCode = HttpStatusCode.RequestTimeout;
+ content = "Request Timeout";
+ }
+ catch (HttpRequestException e)
+ {
+ content = e.Message;
+ }
+ catch (Exception e)
+ {
+ Logger.LogError("An error occured while sending a webhook request", e);
+ }
+ finally
+ {
+ await _webhookManager.StoreResponseOnWebhookSendAttemptAsync(webhookSendAttemptId, webhookSenderArgs.TenantId, statusCode, content);
+ }
+
+ if (!isSucceed)
+ {
+ throw new Exception($"Webhook sending attempt failed. WebhookSendAttempt id: {webhookSendAttemptId}");
+ }
+
+ return webhookSendAttemptId;
+ }
+
+ ///
+ /// You can override this to change request message
+ ///
+ ///
+ protected virtual HttpRequestMessage CreateWebhookRequestMessage(WebhookSenderArgs webhookSenderArgs)
+ {
+ return new HttpRequestMessage(HttpMethod.Post, webhookSenderArgs.WebhookUri);
+ }
+
+ protected virtual void AddAdditionalHeaders(HttpRequestMessage request, WebhookSenderArgs webhookSenderArgs)
+ {
+ foreach (var header in webhookSenderArgs.Headers)
+ {
+ if (request.Headers.TryAddWithoutValidation(header.Key, header.Value))
+ {
+ continue;
+ }
+
+ if (request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value))
+ {
+ continue;
+ }
+
+ throw new Exception($"Invalid Header. SubscriptionId:{webhookSenderArgs.WebhookSubscriptionId},Header: {header.Key}:{header.Value}");
+ }
+ }
+
+ protected virtual async Task<(bool isSucceed, HttpStatusCode statusCode, string content)> SendHttpRequest(HttpRequestMessage request)
+ {
+ using (var client = new HttpClient
+ {
+ Timeout = _options.TimeoutDuration
+ })
+ {
+ var response = await client.SendAsync(request);
+
+ var isSucceed = response.IsSuccessStatusCode;
+ var statusCode = response.StatusCode;
+ var content = await response.Content.ReadAsStringAsync();
+
+ return (isSucceed, statusCode, content);
+ }
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs
new file mode 100644
index 000000000..328c214bd
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs
@@ -0,0 +1,22 @@
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookDefinitionContext
+ {
+ ///
+ /// Adds the specified webhook definition. Throws exception if it is already added
+ ///
+ void Add(params WebhookDefinition[] definitions);
+
+ ///
+ /// Gets a webhook definition by name.
+ /// Returns null if there is no webhook definition with given name.
+ ///
+ WebhookDefinition GetOrNull(string name);
+
+ ///
+ /// Remove webhook with given name
+ ///
+ /// webhook definition name
+ void Remove(string name);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs
new file mode 100644
index 000000000..057b0b984
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookDefinitionManager
+ {
+ ///
+ /// Gets a webhook definition by name.
+ /// Returns null if there is no webhook definition with given name.
+ ///
+ WebhookDefinition GetOrNull(string name);
+
+ ///
+ /// Gets a webhook definition by name.
+ /// Throws exception if there is no webhook definition with given name.
+ ///
+ WebhookDefinition Get(string name);
+
+ ///
+ /// Gets all webhook definitions.
+ ///
+ IReadOnlyList GetAll();
+
+ ///
+ /// Checks if given webhook name is available for given tenant.
+ ///
+ Task IsAvailableAsync(Guid? tenantId, string name);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs
new file mode 100644
index 000000000..1fb8f5c21
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookEventStore
+ {
+ ///
+ /// Inserts to persistent store
+ ///
+ Task InsertAndGetIdAsync(WebhookEvent webhookEvent);
+
+ ///
+ /// Gets Webhook info by id
+ ///
+ Task GetAsync(Guid? tenantId, Guid id);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs
new file mode 100644
index 000000000..1e8e5fd0c
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookManager
+ {
+ Task GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs);
+
+ void SignWebhookRequest(HttpRequestMessage request, string serializedBody, string secret);
+
+ Task GetSerializedBodyAsync(WebhookSenderArgs webhookSenderArgs);
+
+ Task InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs);
+
+ Task StoreResponseOnWebhookSendAttemptAsync(
+ Guid webhookSendAttemptId, Guid? tenantId,
+ HttpStatusCode? statusCode, string content);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs
new file mode 100644
index 000000000..3b53fec83
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookPublisher
+ {
+ ///
+ /// Sends webhooks to current tenant subscriptions (). with given data, (Checks permissions)
+ ///
+ ///
+ /// data to send
+ ///
+ /// True: It sends the exact same data as the parameter to clients.
+ ///
+ /// False: It sends data in . It is recommended way.
+ ///
+ ///
+ /// Headers to send. Publisher uses subscription defined webhook by default. You can add additional headers from here. If subscription already has given header, publisher uses the one you give here.
+ Task PublishAsync(string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null);
+
+ ///
+ /// Sends webhooks to given tenant's subscriptions
+ ///
+ ///
+ /// data to send
+ ///
+ /// Target tenant id
+ ///
+ ///
+ /// True: It sends the exact same data as the parameter to clients.
+ ///
+ /// False: It sends data in . It is recommended way.
+ ///
+ ///
+ /// Headers to send. Publisher uses subscription defined webhook by default. You can add additional headers from here. If subscription already has given header, publisher uses the one you give here.
+ Task PublishAsync(string webhookName, object data, Guid? tenantId, bool sendExactSameData = false, WebhookHeader headers = null);
+
+ ///
+ /// Sends webhooks to given tenant's subscriptions
+ ///
+ ///
+ /// data to send
+ ///
+ /// Target tenant id(s)
+ ///
+ ///
+ /// True: It sends the exact same data as the parameter to clients.
+ ///
+ /// False: It sends data in . It is recommended way.
+ ///
+ ///
+ /// Headers to send. Publisher uses subscription defined webhook by default. You can add additional headers from here. If subscription already has given header, publisher uses the one you give here.
+ Task PublishAsync(Guid?[] tenantIds, string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null);
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs
new file mode 100644
index 000000000..2e32bc63f
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookSendAttemptStore
+ {
+ Task GetAsync(Guid? tenantId, Guid id);
+
+ ///
+ /// Returns work item count by given web hook id and subscription id, (How many times publisher tried to send web hook)
+ ///
+ Task GetSendAttemptCountAsync(Guid? tenantId, Guid webhookId, Guid webhookSubscriptionId);
+
+ ///
+ /// Checks is there any successful webhook attempt in last items. Should return true if there are not X number items
+ ///
+ Task HasXConsecutiveFailAsync(Guid? tenantId, Guid subscriptionId, int searchCount);
+
+ Task<(int TotalCount, IReadOnlyCollection Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount, int skipCount);
+
+ Task> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs
new file mode 100644
index 000000000..d30e110e6
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookSender
+ {
+ ///
+ /// Tries to send webhook with given transactionId and stores process in
+ /// Should throw exception if fails or response status not succeed
+ ///
+ /// arguments
+ /// Webhook send attempt id
+ Task SendWebhookAsync(WebhookSenderArgs webhookSenderArgs);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs
new file mode 100644
index 000000000..956b2dbd3
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public interface IWebhookSubscriptionManager
+ {
+ ///
+ /// Returns subscription for given id.
+ ///
+ /// Unique identifier of
+ Task GetAsync(Guid id);
+
+ ///
+ /// Returns all subscriptions of tenant
+ ///
+ ///
+ /// Target tenant id.
+ ///
+ Task> GetAllSubscriptionsAsync(Guid? tenantId);
+
+ ///
+ /// Returns all subscriptions for given webhook.
+ ///
+ ///
+ ///
+ /// Target tenant id.
+ ///
+ Task> GetAllSubscriptionsIfFeaturesGrantedAsync(Guid? tenantId, string webhookName);
+
+ ///
+ /// Returns all subscriptions of tenant
+ ///
+ ///
+ Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds);
+
+ ///
+ /// Returns all subscriptions for given webhook.
+ ///
+ ///
+ ///
+ /// Target tenant id(s).
+ ///
+ Task> GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(Guid?[] tenantIds, string webhookName);
+
+ ///
+ /// Checks if tenant subscribed for a webhook. (Checks if webhook features are granted)
+ ///
+ ///
+ /// Target tenant id(s).
+ ///
+ ///
+ Task IsSubscribedAsync(Guid? tenantId, string webhookName);
+
+ ///
+ /// If id is the default(Guid) adds new subscription, else updates current one. (Checks if webhook features are granted)
+ ///
+ Task AddOrUpdateSubscriptionAsync(WebhookSubscriptionInfo webhookSubscription);
+
+ ///
+ /// Activates/Deactivates given webhook subscription
+ ///
+ /// unique identifier of
+ /// IsActive
+ Task ActivateWebhookSubscriptionAsync(Guid id, bool active);
+
+ ///
+ /// Delete given webhook subscription.
+ ///
+ /// unique identifier of
+ Task DeleteSubscriptionAsync(Guid id);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs
new file mode 100644
index 000000000..23849ad4e
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ ///
+ /// This interface should be implemented by vendors to make webhooks working.
+ ///
+ public interface IWebhookSubscriptionsStore
+ {
+ ///
+ /// returns subscription
+ ///
+ /// webhook subscription id
+ ///
+ Task GetAsync(Guid id);
+
+ ///
+ /// Saves webhook subscription to a persistent store.
+ ///
+ /// webhook subscription information
+ Task InsertAsync(WebhookSubscriptionInfo webhookSubscription);
+
+ ///
+ /// Updates webhook subscription to a persistent store.
+ ///
+ /// webhook subscription information
+ Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription);
+
+ ///
+ /// Deletes subscription if exists
+ ///
+ /// primary key
+ ///
+ Task DeleteAsync(Guid id);
+
+ ///
+ /// Returns all subscriptions of given tenant including deactivated
+ ///
+ ///
+ /// Target tenant id.
+ ///
+ Task> GetAllSubscriptionsAsync(Guid? tenantId);
+
+ ///
+ /// Returns webhook subscriptions which subscribe to given webhook on tenant(s)
+ ///
+ ///
+ /// Target tenant id.
+ ///
+ ///
+ ///
+ Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName);
+
+ ///
+ /// Returns all subscriptions of given tenant including deactivated
+ ///
+ ///
+ /// Target tenant id(s).
+ ///
+ Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds);
+
+ ///
+ /// Returns webhook subscriptions which subscribe to given webhook on tenant(s)
+ ///
+ ///
+ /// Target tenant id(s).
+ ///
+ ///
+ ///
+ Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName);
+
+ ///
+ /// Checks if tenant subscribed for a webhook
+ ///
+ ///
+ /// Target tenant id(s).
+ ///
+ /// Name of the webhook
+ Task IsSubscribedAsync(Guid? tenantId, string webhookName);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs
new file mode 100644
index 000000000..069726e67
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ ///
+ /// Null pattern implementation of .
+ /// It's used if is not implemented by actual persistent store
+ ///
+ public class NullWebhookEventStore : IWebhookEventStore
+ {
+ public static NullWebhookEventStore Instance { get; } = new NullWebhookEventStore();
+
+ public Task InsertAndGetIdAsync(WebhookEvent webhookEvent)
+ {
+ return Task.FromResult(default);
+ }
+
+ public Task GetAsync(Guid? tenantId, Guid id)
+ {
+ return Task.FromResult(default);
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
new file mode 100644
index 000000000..75749401b
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class NullWebhookSendAttemptStore : IWebhookSendAttemptStore
+ {
+ public static NullWebhookSendAttemptStore Instance = new NullWebhookSendAttemptStore();
+
+ public Task InsertAsync(WebhookSendAttempt webhookSendAttempt)
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task UpdateAsync(WebhookSendAttempt webhookSendAttempt)
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task GetAsync(Guid? tenantId, Guid id)
+ {
+ return Task.FromResult(default);
+ }
+
+ public Task GetSendAttemptCountAsync(Guid? tenantId, Guid webhookId, Guid webhookSubscriptionId)
+ {
+ return Task.FromResult(int.MaxValue);
+ }
+
+ public Task HasXConsecutiveFailAsync(Guid? tenantId, Guid subscriptionId, int searchCount)
+ {
+ return default;
+ }
+
+ public Task> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount,
+ int skipCount)
+ {
+ return Task.FromResult(new List() as IReadOnlyCollection);
+ }
+
+ public Task> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId)
+ {
+ return Task.FromResult(new List());
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs
new file mode 100644
index 000000000..4214d1d25
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ ///
+ /// Null pattern implementation of .
+ /// It's used if is not implemented by actual persistent store
+ ///
+ public class NullWebhookSubscriptionsStore : IWebhookSubscriptionsStore
+ {
+ public static NullWebhookSubscriptionsStore Instance { get; } = new NullWebhookSubscriptionsStore();
+
+ public Task GetAsync(Guid id)
+ {
+ return Task.FromResult(default);
+ }
+
+ public WebhookSubscriptionInfo Get(Guid id)
+ {
+ return default;
+ }
+
+ public Task InsertAsync(WebhookSubscriptionInfo webhookSubscription)
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription)
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task DeleteAsync(Guid id)
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task> GetAllSubscriptionsAsync(Guid? tenantId)
+ {
+ return Task.FromResult(new List());
+ }
+
+ public Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName)
+ {
+ return Task.FromResult(new List());
+ }
+
+ public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
+ {
+ return Task.FromResult(new List());
+ }
+
+ public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName)
+ {
+ return Task.FromResult(new List());
+ }
+
+ public Task IsSubscribedAsync(Guid? tenantId, string webhookName)
+ {
+ return Task.FromResult(false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs
new file mode 100644
index 000000000..88f3a074d
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class WebhookDefinition
+ {
+ ///
+ /// Unique name of the webhook.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Display name of the webhook.
+ /// Optional.
+ ///
+ public ILocalizableString DisplayName { get; set; }
+
+ ///
+ /// Description for the webhook.
+ /// Optional.
+ ///
+ public ILocalizableString Description { get; set; }
+
+ public List RequiredFeatures { get; set; }
+
+ public WebhookDefinition(string name, ILocalizableString displayName = null, ILocalizableString description = null)
+ {
+ if (name.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(name), $"{nameof(name)} can not be null, empty or whitespace!");
+ }
+
+ Name = name.Trim();
+ DisplayName = displayName;
+ Description = description;
+
+ RequiredFeatures = new List();
+ }
+
+ public WebhookDefinition WithFeature(params string[] features)
+ {
+ if (!features.IsNullOrEmpty())
+ {
+ RequiredFeatures.AddRange(features);
+ }
+
+ return this;
+ }
+
+ public override string ToString()
+ {
+ return $"[{nameof(WebhookDefinition)} {Name}]";
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs
new file mode 100644
index 000000000..4176fc785
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs
@@ -0,0 +1,54 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+using Volo.Abp;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class WebhookDefinitionContext : IWebhookDefinitionContext
+ {
+ protected Dictionary Webhooks { get; }
+
+ public WebhookDefinitionContext(Dictionary webhooks)
+ {
+ Webhooks = webhooks;
+ }
+
+ public void Add(params WebhookDefinition[] definitions)
+ {
+ if (definitions.IsNullOrEmpty())
+ {
+ return;
+ }
+
+ foreach (var definition in definitions)
+ {
+ Webhooks[definition.Name] = definition;
+ }
+ }
+
+ public WebhookDefinition GetOrNull([NotNull] string name)
+ {
+ Check.NotNull(name, nameof(name));
+
+ if (!Webhooks.ContainsKey(name))
+ {
+ return null;
+ }
+
+ return Webhooks[name];
+ }
+
+ public void Remove(string name)
+ {
+ Check.NotNull(name, nameof(name));
+
+ if (!Webhooks.ContainsKey(name))
+ {
+ throw new AbpException($"Undefined notification webhook: '{name}'.");
+ }
+
+ Webhooks.Remove(name);
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs
new file mode 100644
index 000000000..bf70017be
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs
@@ -0,0 +1,109 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Features;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonDependency
+ {
+ protected IDictionary WebhookDefinitions => _lazyWebhookDefinitions.Value;
+ private readonly Lazy> _lazyWebhookDefinitions;
+
+ private readonly IServiceProvider _serviceProvider;
+ private readonly AbpWebhooksOptions _options;
+
+ public WebhookDefinitionManager(
+ IServiceProvider serviceProvider,
+ IOptions options)
+ {
+ _serviceProvider = serviceProvider;
+ _options = options.Value;
+
+ _lazyWebhookDefinitions = new Lazy>(CreateWebhookDefinitions);
+ }
+
+ public WebhookDefinition GetOrNull(string name)
+ {
+ if (!WebhookDefinitions.ContainsKey(name))
+ {
+ return null;
+ }
+
+ return WebhookDefinitions[name];
+ }
+
+ public WebhookDefinition Get(string name)
+ {
+ if (!WebhookDefinitions.ContainsKey(name))
+ {
+ throw new KeyNotFoundException($"Webhook definitions does not contain a definition with the key \"{name}\".");
+ }
+
+ return WebhookDefinitions[name];
+ }
+
+ public IReadOnlyList GetAll()
+ {
+ return WebhookDefinitions.Values.ToImmutableList();
+ }
+
+ public async Task IsAvailableAsync(Guid? tenantId, string name)
+ {
+ if (tenantId == null) // host allowed to subscribe all webhooks
+ {
+ return true;
+ }
+
+ var webhookDefinition = GetOrNull(name);
+
+ if (webhookDefinition == null)
+ {
+ return false;
+ }
+
+ if (webhookDefinition.RequiredFeatures?.Any() == false)
+ {
+ return true;
+ }
+
+ var currentTenant = _serviceProvider.GetRequiredService();
+ var featureChecker = _serviceProvider.GetRequiredService();
+ using (currentTenant.Change(tenantId))
+ {
+ if (!await featureChecker.IsEnabledAsync(true, webhookDefinition.RequiredFeatures.ToArray()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected virtual Dictionary CreateWebhookDefinitions()
+ {
+ var definitions = new Dictionary();
+
+ using (var scope = _serviceProvider.CreateScope())
+ {
+ var providers = _options
+ .DefinitionProviders
+ .Select(p => scope.ServiceProvider.GetRequiredService(p) as WebhookDefinitionProvider)
+ .ToList();
+
+ foreach (var provider in providers)
+ {
+ provider.Define(new WebhookDefinitionContext(definitions));
+ }
+ }
+
+ return definitions;
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs
new file mode 100644
index 000000000..ead9ab679
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public abstract class WebhookDefinitionProvider : ITransientDependency
+ {
+ ///
+ /// Used to add/manipulate webhook definitions.
+ ///
+ /// Context,
+ public abstract void Define(IWebhookDefinitionContext context);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs
new file mode 100644
index 000000000..bd32fa76d
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ ///
+ /// Store created web hooks. To see who get that webhook check with and you can get
+ ///
+ public class WebhookEvent
+ {
+ public Guid Id { get; set; }
+
+ ///
+ /// Webhook unique name
+ ///
+ public string WebhookName { get; set; }
+
+ ///
+ /// Webhook data as JSON string.
+ ///
+ public string Data { get; set; }
+
+ public DateTime CreationTime { get; set; }
+
+ public Guid? TenantId { get; set; }
+
+ public bool IsDeleted { get; set; }
+
+ public DateTime? DeletionTime { get; set; }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs
new file mode 100644
index 000000000..91442552f
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class WebhookHeader
+ {
+ ///
+ /// If true, webhook will only contain given headers. If false given headers will be added to predefined headers in subscription.
+ /// Default is false
+ ///
+ public bool UseOnlyGivenHeaders { get; set; }
+
+ ///
+ /// That headers will be sent with the webhook.
+ ///
+ public IDictionary Headers { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
new file mode 100644
index 000000000..29389e15e
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Globalization;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using Volo.Abp.Json;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public abstract class WebhookManager : IWebhookManager
+ {
+ private const string SignatureHeaderKey = "sha256";
+ private const string SignatureHeaderValueTemplate = SignatureHeaderKey + "={0}";
+ private const string SignatureHeaderName = "abp-webhook-signature";
+
+ protected IJsonSerializer JsonSerializer { get; }
+ protected IWebhookSendAttemptStore WebhookSendAttemptStore { get; }
+
+ protected WebhookManager(
+ IJsonSerializer jsonSerializer,
+ IWebhookSendAttemptStore webhookSendAttemptStore)
+ {
+ JsonSerializer = jsonSerializer;
+ WebhookSendAttemptStore = webhookSendAttemptStore;
+ }
+
+ public virtual async Task GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs)
+ {
+ var data = JsonSerializer.Serialize(webhookSenderArgs.Data);
+
+ var attemptNumber = await WebhookSendAttemptStore.GetSendAttemptCountAsync(
+ webhookSenderArgs.TenantId,
+ webhookSenderArgs.WebhookEventId,
+ webhookSenderArgs.WebhookSubscriptionId);
+
+ return new WebhookPayload(
+ webhookSenderArgs.WebhookEventId.ToString(),
+ webhookSenderArgs.WebhookName,
+ attemptNumber)
+ {
+ Data = data
+ };
+ }
+
+ public virtual void SignWebhookRequest(HttpRequestMessage request, string serializedBody, string secret)
+ {
+ if (request == null)
+ {
+ throw new ArgumentNullException(nameof(request));
+ }
+
+ if (string.IsNullOrWhiteSpace(serializedBody))
+ {
+ throw new ArgumentNullException(nameof(serializedBody));
+ }
+
+ request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json");
+
+ var secretBytes = Encoding.UTF8.GetBytes(secret);
+ var headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, serializedBody.Sha256(secretBytes));
+
+ request.Headers.Add(SignatureHeaderName, headerValue);
+ }
+
+ public virtual async Task GetSerializedBodyAsync(WebhookSenderArgs webhookSenderArgs)
+ {
+ if (webhookSenderArgs.SendExactSameData)
+ {
+ return webhookSenderArgs.Data;
+ }
+
+ var payload = await GetWebhookPayloadAsync(webhookSenderArgs);
+
+ var serializedBody = JsonSerializer.Serialize(payload);
+
+ return serializedBody;
+ }
+
+ public abstract Task InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs);
+
+ public abstract Task StoreResponseOnWebhookSendAttemptAsync(Guid webhookSendAttemptId, Guid? tenantId, HttpStatusCode? statusCode, string content);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs
new file mode 100644
index 000000000..ff287cbd0
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class WebhookPayload
+ {
+ public string Id { get; set; }
+
+ public string WebhookEvent { get; set; }
+
+ public int Attempt { get; set; }
+
+ public dynamic Data { get; set; }
+
+ public DateTime CreationTimeUtc { get; set; }
+
+ public WebhookPayload(string id, string webhookEvent, int attempt)
+ {
+ if (id.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(id));
+ }
+
+ if (webhookEvent.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(webhookEvent));
+ }
+
+ Id = id;
+ WebhookEvent = webhookEvent;
+ Attempt = attempt;
+ CreationTimeUtc = DateTime.UtcNow;
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs
new file mode 100644
index 000000000..8189f8f72
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Net;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ ///
+ /// Table for store webhook work items. Each item stores web hook send attempt of to subscribed tenants
+ ///
+ public class WebhookSendAttempt
+ {
+ public Guid Id { get; set; }
+
+ ///
+ /// foreign id
+ ///
+ public Guid WebhookEventId { get; set; }
+
+ ///
+ /// foreign id
+ ///
+ public Guid WebhookSubscriptionId { get; set; }
+
+ ///
+ /// Webhook response content that webhook endpoint send back
+ ///
+ public string Response { get; set; }
+
+ ///
+ /// Webhook response status code that webhook endpoint send back
+ ///
+ public HttpStatusCode? ResponseStatusCode { get; set; }
+
+ public DateTime CreationTime { get; set; }
+
+ public DateTime? LastModificationTime { get; set; }
+
+ public Guid? TenantId { get; set; }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs
new file mode 100644
index 000000000..7910ba0b1
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class WebhookSenderArgs
+ {
+ public Guid? TenantId { get; set; }
+
+ //Webhook information
+
+ ///
+ /// foreign id
+ ///
+ public Guid WebhookEventId { get; set; }
+
+ ///
+ /// Webhook unique name
+ ///
+ public string WebhookName { get; set; }
+
+ ///
+ /// Webhook data as JSON string.
+ ///
+ public string Data { get; set; }
+
+ //Subscription information
+
+ ///
+ /// foreign id
+ ///
+ public Guid WebhookSubscriptionId { get; set; }
+
+ ///
+ /// Subscription webhook endpoint
+ ///
+ public string WebhookUri { get; set; }
+
+ ///
+ /// Webhook secret
+ ///
+ public string Secret { get; set; }
+
+ ///
+ /// Gets a set of additional HTTP headers.That headers will be sent with the webhook.
+ ///
+ public IDictionary Headers { get; set; }
+
+ ///
+ /// Tries to send webhook only one time without checking to send attempt count
+ ///
+ public bool TryOnce { get; set; }
+
+ ///
+ /// True: It sends the exact same data as the parameter to clients.
+ ///
+ /// False: It sends data in . It is recommended way.
+ ///
+ ///
+ public bool SendExactSameData { get; set; }
+
+ public WebhookSenderArgs()
+ {
+ Headers = new Dictionary();
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs
new file mode 100644
index 000000000..9e4e3bf2a
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class WebhookSubscriptionInfo
+ {
+ public Guid Id { get; set; }
+ ///
+ /// Subscribed Tenant's id .
+ ///
+ public Guid? TenantId { get; set; }
+
+ ///
+ /// Subscription webhook endpoint
+ ///
+ public string WebhookUri { get; set; }
+
+ ///
+ /// Webhook secret
+ ///
+ public string Secret { get; set; }
+
+ ///
+ /// Is subscription active
+ ///
+ public bool IsActive { get; set; }
+
+ ///
+ /// Subscribed webhook definitions unique names.It contains webhook definitions list as json
+ ///
+ /// Do not change it manually.
+ /// Use ,
+ /// ,
+ /// and
+ /// to change it.
+ ///
+ ///
+ public List Webhooks { get; set; }
+
+ ///
+ /// Gets a set of additional HTTP headers.That headers will be sent with the webhook. It contains webhook header dictionary as json
+ ///
+ /// Do not change it manually.
+ /// Use ,
+ /// ,
+ /// ,
+ /// to change it.
+ ///
+ ///
+ public IDictionary Headers { get; set; }
+
+ public WebhookSubscriptionInfo()
+ {
+ IsActive = true;
+ Headers = new Dictionary();
+ Webhooks = new List();
+ }
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
new file mode 100644
index 000000000..4ed53d52b
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp.Authorization;
+using Volo.Abp.Guids;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.Webhooks
+{
+ public class WebhookSubscriptionManager : IWebhookSubscriptionManager
+ {
+ public IWebhookSubscriptionsStore WebhookSubscriptionsStore { get; set; }
+
+ private readonly IGuidGenerator _guidGenerator;
+ private readonly IUnitOfWorkManager _unitOfWorkManager;
+ private readonly IWebhookDefinitionManager _webhookDefinitionManager;
+
+ private const string WebhookSubscriptionSecretPrefix = "whs_";
+
+ public WebhookSubscriptionManager(
+ IGuidGenerator guidGenerator,
+ IUnitOfWorkManager unitOfWorkManager,
+ IWebhookDefinitionManager webhookDefinitionManager)
+ {
+ _guidGenerator = guidGenerator;
+ _unitOfWorkManager = unitOfWorkManager;
+ _webhookDefinitionManager = webhookDefinitionManager;
+
+ WebhookSubscriptionsStore = NullWebhookSubscriptionsStore.Instance;
+ }
+
+ public virtual async Task GetAsync(Guid id)
+ {
+ return await WebhookSubscriptionsStore.GetAsync(id);
+ }
+
+ public virtual async Task> GetAllSubscriptionsAsync(Guid? tenantId)
+ {
+ return await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId);
+ }
+
+ public virtual async Task> GetAllSubscriptionsIfFeaturesGrantedAsync(Guid? tenantId, string webhookName)
+ {
+ if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName))
+ {
+ return new List();
+ }
+
+ return (await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId, webhookName)).ToList();
+ }
+
+ public virtual async Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
+ {
+ return (await WebhookSubscriptionsStore.GetAllSubscriptionsOfTenantsAsync(tenantIds)).ToList();
+ }
+
+ public virtual async Task> GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(Guid?[] tenantIds, string webhookName)
+ {
+ var featureGrantedTenants = new List();
+ foreach (var tenantId in tenantIds)
+ {
+ if (await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName))
+ {
+ featureGrantedTenants.Add(tenantId);
+ }
+ }
+
+ return (await WebhookSubscriptionsStore.GetAllSubscriptionsOfTenantsAsync(featureGrantedTenants.ToArray(), webhookName)).ToList();
+ }
+
+ public virtual async Task IsSubscribedAsync(Guid? tenantId, string webhookName)
+ {
+ if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName))
+ {
+ return false;
+ }
+
+ return await WebhookSubscriptionsStore.IsSubscribedAsync(tenantId, webhookName);
+ }
+
+ public virtual async Task AddOrUpdateSubscriptionAsync(WebhookSubscriptionInfo webhookSubscription)
+ {
+ using (var uow = _unitOfWorkManager.Begin())
+ {
+ await CheckIfPermissionsGrantedAsync(webhookSubscription);
+
+ if (webhookSubscription.Id == default)
+ {
+ webhookSubscription.Id = _guidGenerator.Create();
+ webhookSubscription.Secret = WebhookSubscriptionSecretPrefix + Guid.NewGuid().ToString("N");
+ await WebhookSubscriptionsStore.InsertAsync(webhookSubscription);
+ }
+ else
+ {
+ var subscription = await WebhookSubscriptionsStore.GetAsync(webhookSubscription.Id);
+ subscription.WebhookUri = webhookSubscription.WebhookUri;
+ subscription.Webhooks = webhookSubscription.Webhooks;
+ subscription.Headers = webhookSubscription.Headers;
+ await WebhookSubscriptionsStore.UpdateAsync(subscription);
+ }
+
+ await uow.SaveChangesAsync();
+ }
+ }
+
+ public virtual async Task ActivateWebhookSubscriptionAsync(Guid id, bool active)
+ {
+ using (var uow = _unitOfWorkManager.Begin())
+ {
+ var webhookSubscription = await WebhookSubscriptionsStore.GetAsync(id);
+ webhookSubscription.IsActive = active;
+
+ await uow.SaveChangesAsync();
+ }
+ }
+
+ public virtual async Task DeleteSubscriptionAsync(Guid id)
+ {
+ using (var uow = _unitOfWorkManager.Begin())
+ {
+ await WebhookSubscriptionsStore.DeleteAsync(id);
+
+ await uow.SaveChangesAsync();
+ }
+ }
+
+ public virtual async Task AddWebhookAsync(WebhookSubscriptionInfo subscription, string webhookName)
+ {
+ using (var uow = _unitOfWorkManager.Begin())
+ {
+ await CheckPermissionsAsync(subscription.TenantId, webhookName);
+ webhookName = webhookName.Trim();
+ if (webhookName.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(webhookName), $"{nameof(webhookName)} can not be null, empty or whitespace!");
+ }
+
+ if (!subscription.Webhooks.Contains(webhookName))
+ {
+ subscription.Webhooks.Add(webhookName);
+
+ await WebhookSubscriptionsStore.UpdateAsync(subscription);
+ }
+
+ await uow.SaveChangesAsync();
+ }
+ }
+
+ #region PermissionCheck
+
+ protected virtual async Task CheckIfPermissionsGrantedAsync(WebhookSubscriptionInfo webhookSubscription)
+ {
+ if (webhookSubscription.Webhooks.IsNullOrEmpty())
+ {
+ return;
+ }
+
+ foreach (var webhookDefinition in webhookSubscription.Webhooks)
+ {
+ await CheckPermissionsAsync(webhookSubscription.TenantId, webhookDefinition);
+ }
+ }
+
+ protected virtual async Task CheckPermissionsAsync(Guid? tenantId, string webhookName)
+ {
+ if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName))
+ {
+ throw new AbpAuthorizationException($"Tenant \"{tenantId}\" must have necessary feature(s) to use webhook \"{webhookName}\"");
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs
new file mode 100644
index 000000000..f4202f6e2
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs
@@ -0,0 +1,13 @@
+using System.Security.Cryptography;
+
+namespace System;
+
+internal static class AbpStringCryptographyExtensions
+{
+ public static string Sha256(this string planText, byte[] salt)
+ {
+ var data = planText.GetBytes();
+ using var hmacsha256 = new HMACSHA256(salt);
+ return BitConverter.ToString(hmacsha256.ComputeHash(data));
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj
new file mode 100644
index 000000000..70d317b54
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs
new file mode 100644
index 000000000..b9295955e
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs
@@ -0,0 +1,22 @@
+using LINGYUN.Abp.WebhooksManagement.Localization;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.WebhooksManagement.Authorization;
+
+public class WebhooksManagementPermissionDefinitionProvider : PermissionDefinitionProvider
+{
+ public override void Define(IPermissionDefinitionContext context)
+ {
+ var group = context.AddGroup(WebhooksManagementPermissions.GroupName, L("Permission:WebhooksManagement"));
+
+ group.AddPermission(
+ WebhooksManagementPermissions.ManageSettings,
+ L("Permission:ManageSettings"));
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs
new file mode 100644
index 000000000..0a1d9b0f7
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs
@@ -0,0 +1,8 @@
+namespace LINGYUN.Abp.WebhooksManagement.Authorization;
+
+public static class WebhooksManagementPermissions
+{
+ public const string GroupName = "WebhooksManagement";
+
+ public const string ManageSettings = GroupName + ".ManageSettings";
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs
new file mode 100644
index 000000000..22d14bd79
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs
@@ -0,0 +1,18 @@
+using LINGYUN.Abp.WebhooksManagement.Localization;
+using Volo.Abp.Features;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.WebhooksManagement.Features;
+
+public class WebhooksManagementFeatureDefinitionProvider : FeatureDefinitionProvider
+{
+ public override void Define(IFeatureDefinitionContext context)
+ {
+ var group = context.AddGroup(WebhooksManagementFeatureNames.GroupName, L("Features:WebhooksManagement"));
+ }
+
+ private static ILocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs
new file mode 100644
index 000000000..6cb71bf6d
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs
@@ -0,0 +1,6 @@
+namespace LINGYUN.Abp.WebhooksManagement.Features;
+
+public static class WebhooksManagementFeatureNames
+{
+ public const string GroupName = "WebhooksManagement";
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs
new file mode 100644
index 000000000..977589b9c
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs
@@ -0,0 +1,15 @@
+using Volo.Abp.Application;
+using Volo.Abp.Authorization;
+using Volo.Abp.Features;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpFeaturesModule),
+ typeof(AbpAuthorizationModule),
+ typeof(AbpDddApplicationContractsModule),
+ typeof(WebhooksManagementDomainSharedModule))]
+public class WebhooksManagementApplicationContractsModule : AbpModule
+{
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs
new file mode 100644
index 000000000..f05d02644
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs
@@ -0,0 +1,7 @@
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public static class WebhooksManagementRemoteServiceConsts
+{
+ public const string RemoteServiceName = "WebhooksManagement";
+ public const string ModuleName = "WebhooksManagement";
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj
new file mode 100644
index 000000000..24d0a91e7
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs
new file mode 100644
index 000000000..3953ab3e0
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs
@@ -0,0 +1,13 @@
+using LINGYUN.Abp.WebhooksManagement.Localization;
+using Volo.Abp.Application.Services;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public abstract class WebhooksManagementAppServiceBase : ApplicationService
+{
+ protected WebhooksManagementAppServiceBase()
+ {
+ LocalizationResource = typeof(WebhooksManagementResource);
+ ObjectMapperContext = typeof(WebhooksManagementApplicationModule);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs
new file mode 100644
index 000000000..dd3323fec
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs
@@ -0,0 +1,10 @@
+using AutoMapper;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhooksManagementApplicationMapperProfile : Profile
+{
+ public WebhooksManagementApplicationMapperProfile()
+ {
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs
new file mode 100644
index 000000000..eef27929b
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs
@@ -0,0 +1,24 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Application;
+using Volo.Abp.Authorization;
+using Volo.Abp.AutoMapper;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpAuthorizationModule),
+ typeof(AbpDddApplicationModule),
+ typeof(WebhooksManagementDomainModule))]
+public class WebhooksManagementApplicationModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAutoMapperObjectMapper();
+
+ Configure(options =>
+ {
+ options.AddProfile(validate: true);
+ });
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj
new file mode 100644
index 000000000..e07d4c79d
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs
new file mode 100644
index 000000000..bb89de573
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs
@@ -0,0 +1,18 @@
+using LINGYUN.Abp.Dapr.Client;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpDaprClientModule),
+ typeof(WebhooksManagementApplicationContractsModule))]
+public class WebhooksManagementDaprClientModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddDaprClientProxies(
+ typeof(WebhooksManagementApplicationContractsModule).Assembly,
+ WebhooksManagementRemoteServiceConsts.RemoteServiceName);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj
new file mode 100644
index 000000000..682b8eff5
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json
new file mode 100644
index 000000000..f31721006
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json
@@ -0,0 +1,8 @@
+{
+ "culture": "en",
+ "texts": {
+ "Features:WebhooksManagement": "WebhooksManagement",
+ "Permission:WebhooksManagement": "WebhooksManagement",
+ "Permission:ManageSettings": "Manage Settings"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..759c3f745
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json
@@ -0,0 +1,8 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Features:WebhooksManagement": "WebhooksManagement",
+ "Permission:WebhooksManagement": "WebhooksManagement",
+ "Permission:ManageSettings": "管理设置"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs
new file mode 100644
index 000000000..c95515cad
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.WebhooksManagement.Localization;
+
+[LocalizationResourceName("WebhooksManagement")]
+public class WebhooksManagementResource
+{
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs
new file mode 100644
index 000000000..9b810dd32
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs
@@ -0,0 +1,16 @@
+using System;
+using Volo.Abp.ObjectExtending.Modularity;
+
+namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending;
+
+public class WebhooksManagementModuleExtensionConfiguration : ModuleExtensionConfiguration
+{
+ public WebhooksManagementModuleExtensionConfiguration ConfigureWebhooksManagement(
+ Action configureAction)
+ {
+ return this.ConfigureEntity(
+ WebhooksManagementModuleExtensionConsts.EntityNames.Entity,
+ configureAction
+ );
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs
new file mode 100644
index 000000000..b41e10ccb
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Volo.Abp.ObjectExtending.Modularity;
+
+namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending;
+
+public static class WebhooksManagementModuleExtensionConfigurationDictionaryExtensions
+{
+ public static ModuleExtensionConfigurationDictionary ConfigureWebhooksManagement(
+ this ModuleExtensionConfigurationDictionary modules,
+ Action configureAction)
+ {
+ return modules.ConfigureModule(
+ WebhooksManagementModuleExtensionConsts.ModuleName,
+ configureAction
+ );
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs
new file mode 100644
index 000000000..b16fe772e
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs
@@ -0,0 +1,11 @@
+namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending;
+
+public static class WebhooksManagementModuleExtensionConsts
+{
+ public const string ModuleName = "WebhooksManagement";
+
+ public static class EntityNames
+ {
+ public const string Entity = "Entity";
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs
new file mode 100644
index 000000000..1e9234e7a
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs
@@ -0,0 +1,32 @@
+using LINGYUN.Abp.WebhooksManagement.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Localization.ExceptionHandling;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpLocalizationModule))]
+public class WebhooksManagementDomainSharedModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add()
+ .AddVirtualJson("/LINGYUN/Abp/WebhooksManagement/Localization/Resources");
+ });
+
+ Configure(options =>
+ {
+ options.MapCodeNamespace(WebhooksManagementErrorCodes.Namespace, typeof(WebhooksManagementResource));
+ });
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs
new file mode 100644
index 000000000..a7d2af4f9
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs
@@ -0,0 +1,6 @@
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public static class WebhooksManagementErrorCodes
+{
+ public const string Namespace = "WebhooksManagement";
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj
new file mode 100644
index 000000000..2ba7caa77
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs
new file mode 100644
index 000000000..c2535823c
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs
@@ -0,0 +1,70 @@
+using LINGYUN.Abp.Webhooks;
+using System;
+using System.Net;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Guids;
+using Volo.Abp.Json;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class DefaultWebhookManager : WebhookManager, ITransientDependency
+{
+ protected ICurrentTenant CurrentTenant { get; }
+ protected IGuidGenerator GuidGenerator { get; }
+ protected IUnitOfWorkManager UnitOfWorkManager { get; }
+ protected IWebhookSendRecordRepository WebhookSendAttemptRepository { get; }
+ public DefaultWebhookManager(
+ ICurrentTenant currentTenant,
+ IGuidGenerator guidGenerator,
+ IJsonSerializer jsonSerializer,
+ IWebhookSendAttemptStore webhookSendAttemptStore,
+ IUnitOfWorkManager unitOfWorkManager,
+ IWebhookSendRecordRepository webhookSendAttemptRepository)
+ : base(jsonSerializer, webhookSendAttemptStore)
+ {
+ CurrentTenant = currentTenant;
+ GuidGenerator = guidGenerator;
+ UnitOfWorkManager = unitOfWorkManager;
+ WebhookSendAttemptRepository = webhookSendAttemptRepository;
+ }
+
+ public async override Task InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs)
+ {
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(webhookSenderArgs.TenantId))
+ {
+ var record = new WebhookSendRecord(
+ GuidGenerator.Create(),
+ webhookSenderArgs.WebhookEventId,
+ webhookSenderArgs.WebhookSubscriptionId,
+ webhookSenderArgs.TenantId);
+
+ await WebhookSendAttemptRepository.InsertAsync(record);
+
+ await uow.SaveChangesAsync();
+
+ return record.Id;
+ }
+ }
+ }
+
+ public async override Task StoreResponseOnWebhookSendAttemptAsync(Guid webhookSendAttemptId, Guid? tenantId, HttpStatusCode? statusCode, string content)
+ {
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(tenantId))
+ {
+ var record = await WebhookSendAttemptRepository.GetAsync(webhookSendAttemptId);
+ record.SetResponse(content, statusCode);
+
+ await WebhookSendAttemptRepository.UpdateAsync(record);
+
+ await uow.SaveChangesAsync();
+ }
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs
new file mode 100644
index 000000000..45a69d4f2
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs
@@ -0,0 +1,8 @@
+using System;
+using Volo.Abp.Domain.Repositories;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public interface IWebhookEventRecordRepository : IRepository
+{
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs
new file mode 100644
index 000000000..4cf97509f
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public interface IWebhookSendRecordRepository : IRepository
+{
+ Task GetCountAsync(
+ WebhookSendRecordFilter filter,
+ CancellationToken cancellationToken = default);
+
+ Task> GetListAsync(
+ WebhookSendRecordFilter filter,
+ string sorting = nameof(WebhookSendRecord.CreationTime),
+ int maxResultCount = 10,
+ int skipCount = 10,
+ CancellationToken cancellationToken = default);
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs
new file mode 100644
index 000000000..faf4dbbee
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs
@@ -0,0 +1,8 @@
+using System;
+using Volo.Abp.Domain.Repositories;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public interface IWebhookSubscriptionRepository : IRepository
+{
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs
new file mode 100644
index 000000000..e3aa21264
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs
@@ -0,0 +1,11 @@
+using Volo.Abp.Settings;
+
+namespace LINGYUN.Abp.WebhooksManagement.Settings
+{
+ public class WebhooksManagementSettingDefinitionProvider : SettingDefinitionProvider
+ {
+ public override void Define(ISettingDefinitionContext context)
+ {
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs
new file mode 100644
index 000000000..1de9fcda9
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs
@@ -0,0 +1,7 @@
+namespace LINGYUN.Abp.WebhooksManagement.Settings
+{
+ public static class WebhooksManagementSettings
+ {
+ public const string GroupName = "WebhooksManagement";
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs
new file mode 100644
index 000000000..e48bacf8c
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs
@@ -0,0 +1,30 @@
+using System;
+using Volo.Abp.Auditing;
+using Volo.Abp.Domain.Entities;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhookEventRecord : Entity, IMultiTenant, IHasCreationTime, IHasDeletionTime
+{
+ public virtual Guid? TenantId { get; protected set; }
+ public virtual string WebhookName { get; protected set; }
+ public virtual string Data { get; protected set; }
+ public virtual DateTime CreationTime { get; set; }
+ public virtual DateTime? DeletionTime { get; set; }
+ public virtual bool IsDeleted { get; set; }
+ protected WebhookEventRecord()
+ {
+ }
+
+ public WebhookEventRecord(
+ Guid id,
+ string webhookName,
+ string data,
+ Guid? tenantId = null) : base(id)
+ {
+ WebhookName = webhookName;
+ Data = data;
+ TenantId = tenantId;
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs
new file mode 100644
index 000000000..62d864e8b
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs
@@ -0,0 +1,57 @@
+using LINGYUN.Abp.Webhooks;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Services;
+using Volo.Abp.ObjectMapping;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhookEventStore : DomainService, IWebhookEventStore
+{
+ protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetRequiredService>();
+
+ protected IUnitOfWorkManager UnitOfWorkManager { get; }
+ protected IWebhookEventRecordRepository WebhookEventRepository { get; }
+
+ public WebhookEventStore(
+ IUnitOfWorkManager unitOfWorkManager,
+ IWebhookEventRecordRepository webhookEventRepository)
+ {
+ UnitOfWorkManager = unitOfWorkManager;
+ WebhookEventRepository = webhookEventRepository;
+ }
+
+ public async virtual Task GetAsync(Guid? tenantId, Guid id)
+ {
+ using var uow = UnitOfWorkManager.Begin();
+ using (CurrentTenant.Change(tenantId))
+ {
+ var record = await WebhookEventRepository.GetAsync(id);
+
+ return ObjectMapper.Map(record);
+ }
+ }
+
+ public async virtual Task InsertAndGetIdAsync(WebhookEvent webhookEvent)
+ {
+ using var uow = UnitOfWorkManager.Begin();
+ using (CurrentTenant.Change(webhookEvent.TenantId))
+ {
+ var record = new WebhookEventRecord(
+ GuidGenerator.Create(),
+ webhookEvent.WebhookName,
+ webhookEvent.Data,
+ webhookEvent.TenantId)
+ {
+ CreationTime = Clock.Now,
+ };
+
+ await WebhookEventRepository.InsertAsync(record);
+
+ await uow.SaveChangesAsync();
+
+ return record.Id;
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs
new file mode 100644
index 000000000..8e024decf
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs
@@ -0,0 +1,165 @@
+using LINGYUN.Abp.Webhooks;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+using Volo.Abp.Domain.Services;
+using Volo.Abp.Linq;
+using Volo.Abp.ObjectMapping;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhookSendAttemptStore : DomainService, IWebhookSendAttemptStore
+{
+ protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetRequiredService>();
+ protected IAsyncQueryableExecuter AsyncQueryableExecuter => LazyServiceProvider.LazyGetRequiredService();
+
+ protected IUnitOfWorkManager UnitOfWorkManager { get; }
+ protected IWebhookSendRecordRepository WebhookSendAttemptRepository { get; }
+
+ public WebhookSendAttemptStore(
+ IUnitOfWorkManager unitOfWorkManager,
+ IWebhookSendRecordRepository webhookSendAttemptRepository)
+ {
+ UnitOfWorkManager = unitOfWorkManager;
+ WebhookSendAttemptRepository = webhookSendAttemptRepository;
+ }
+
+ public async virtual Task<(int TotalCount, IReadOnlyCollection Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(
+ Guid? tenantId,
+ Guid subscriptionId,
+ int maxResultCount,
+ int skipCount)
+ {
+ (int TotalCount, IReadOnlyCollection Webhooks) sendAttempts;
+
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(tenantId))
+ {
+ var filter = new WebhookSendRecordFilter
+ {
+ SubscriptionId = subscriptionId,
+ };
+ var totalCount = await WebhookSendAttemptRepository.GetCountAsync(filter);
+
+ var list = await WebhookSendAttemptRepository.GetListAsync(
+ filter,
+ maxResultCount: maxResultCount,
+ skipCount: skipCount);
+
+ var webHooks = ObjectMapper.Map, List>(list);
+
+ sendAttempts = ValueTuple.Create(totalCount, webHooks.ToImmutableList());
+ }
+
+ await uow.CompleteAsync();
+ }
+
+ return sendAttempts;
+ }
+
+ public async virtual Task> GetAllSendAttemptsByWebhookEventIdAsync(
+ Guid? tenantId,
+ Guid webhookEventId)
+ {
+ List sendAttempts;
+
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(tenantId))
+ {
+ var queryable = await WebhookSendAttemptRepository.GetQueryableAsync();
+
+ var list = await AsyncQueryableExecuter.ToListAsync(queryable
+ .Where(attempt => attempt.WebhookEventId == webhookEventId)
+ .OrderByDescending(attempt => attempt.CreationTime)
+ );
+
+ sendAttempts = ObjectMapper.Map, List>(list);
+ }
+
+ await uow.CompleteAsync();
+ }
+
+ return sendAttempts;
+ }
+
+ public async virtual Task GetAsync(
+ Guid? tenantId,
+ Guid id)
+ {
+ WebhookSendRecord sendAttempt;
+
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(tenantId))
+ {
+ sendAttempt = await WebhookSendAttemptRepository.GetAsync(id);
+ }
+
+ await uow.CompleteAsync();
+ }
+
+ return ObjectMapper.Map(sendAttempt);
+ }
+
+ public async virtual Task GetSendAttemptCountAsync(
+ Guid? tenantId,
+ Guid webhookEventId,
+ Guid webhookSubscriptionId)
+ {
+ int sendAttemptCount;
+
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(tenantId))
+ {
+ sendAttemptCount = await WebhookSendAttemptRepository.CountAsync(attempt =>
+ attempt.WebhookEventId == webhookEventId &&
+ attempt.WebhookSubscriptionId == webhookSubscriptionId);
+ }
+
+ await uow.CompleteAsync();
+ }
+
+ return sendAttemptCount;
+ }
+
+ public async virtual Task HasXConsecutiveFailAsync(
+ Guid? tenantId,
+ Guid subscriptionId,
+ int failCount)
+ {
+ bool result;
+
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(tenantId))
+ {
+ if (await WebhookSendAttemptRepository.CountAsync(x => x.WebhookSubscriptionId == subscriptionId) < failCount)
+ {
+ result = false;
+ }
+ else
+ {
+ var queryable = await WebhookSendAttemptRepository.GetQueryableAsync();
+
+ result = !await AsyncQueryableExecuter.AnyAsync(queryable
+ .OrderByDescending(attempt => attempt.CreationTime)
+ .Take(failCount)
+ .Where(attempt => attempt.ResponseStatusCode == HttpStatusCode.OK)
+ );
+ }
+ }
+
+ await uow.CompleteAsync();
+ }
+
+ return result;
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs
new file mode 100644
index 000000000..91c3c8a97
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Net;
+using Volo.Abp.Auditing;
+using Volo.Abp.Domain.Entities;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhookSendRecord : Entity, IHasCreationTime, IHasModificationTime, IMultiTenant
+{
+ public virtual Guid? TenantId { get; protected set; }
+
+ public virtual Guid WebhookEventId { get; protected set; }
+
+ public virtual Guid WebhookSubscriptionId { get; protected set; }
+
+ public virtual string Response { get; protected set; }
+
+ public virtual HttpStatusCode? ResponseStatusCode { get; set; }
+
+ public virtual DateTime CreationTime { get; set; }
+
+ public virtual DateTime? LastModificationTime { get; set; }
+
+ public virtual WebhookEventRecord WebhookEvent { get; protected set; }
+
+ protected WebhookSendRecord()
+ {
+ }
+
+ public WebhookSendRecord(
+ Guid id,
+ Guid eventId,
+ Guid subscriptionId,
+ Guid? tenantId = null) : base(id)
+ {
+ WebhookEventId = eventId;
+ WebhookSubscriptionId = subscriptionId;
+
+ TenantId = tenantId;
+ }
+
+ public void SetResponse(
+ string response,
+ HttpStatusCode? statusCode = null)
+ {
+ Response = response;
+ ResponseStatusCode = statusCode;
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs
new file mode 100644
index 000000000..075a6152b
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Net;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhookSendRecordFilter
+{
+ public string Filter { get; set; }
+
+ public Guid? WebhookEventId { get; set; }
+
+ public Guid? SubscriptionId { get; set; }
+
+ public string Response { get; set; }
+
+ public HttpStatusCode? ResponseStatusCode { get; set; }
+
+ public DateTime? BeginCreationTime { get; set; }
+
+ public DateTime? EndCreationTime { get; set; }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs
new file mode 100644
index 000000000..be342aaa3
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs
@@ -0,0 +1,33 @@
+using System;
+using Volo.Abp.Domain.Entities.Auditing;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhookSubscription : CreationAuditedEntity
+{
+ public virtual Guid? TenantId { get; protected set; }
+ public virtual string WebhookUri { get; protected set; }
+ public virtual string Secret { get; protected set; }
+ public virtual bool IsActive { get; set; }
+ public virtual string Webhooks { get; protected set; }
+ public virtual string Headers { get; protected set; }
+ protected WebhookSubscription()
+ {
+ }
+ public WebhookSubscription(
+ Guid id,
+ string webhookUri,
+ string secret,
+ string webhooks,
+ string headers,
+ Guid? tenantId = null) : base(id)
+ {
+ WebhookUri = webhookUri;
+ Secret = secret;
+ Webhooks = webhooks;
+ Headers = headers;
+ TenantId = tenantId;
+
+ IsActive = true;
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs
new file mode 100644
index 000000000..abdbf33be
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs
@@ -0,0 +1,55 @@
+using LINGYUN.Abp.Webhooks;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Services;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhookSubscriptionsStore : DomainService, IWebhookSubscriptionsStore
+{
+ public Task DeleteAsync(Guid id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task> GetAllSubscriptionsAsync(Guid? tenantId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task GetAsync(Guid id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task InsertAsync(WebhookSubscriptionInfo webhookSubscription)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task IsSubscribedAsync(Guid? tenantId, string webhookName)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs
new file mode 100644
index 000000000..685cccb3e
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs
@@ -0,0 +1,11 @@
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public static class WebhooksManagementDbProperties
+{
+ public static string DbTablePrefix { get; set; } = "WebhooksManagement_";
+
+ public static string DbSchema { get; set; } = null;
+
+
+ public const string ConnectionStringName = "WebhooksManagement";
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs
new file mode 100644
index 000000000..cbef9c444
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs
@@ -0,0 +1,11 @@
+using AutoMapper;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public class WebhooksManagementDomainMapperProfile : Profile
+{
+ public WebhooksManagementDomainMapperProfile()
+ {
+
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs
new file mode 100644
index 000000000..bdeb77779
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs
@@ -0,0 +1,43 @@
+using LINGYUN.Abp.Webhooks;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AutoMapper;
+using Volo.Abp.Domain.Entities.Events.Distributed;
+using Volo.Abp.Modularity;
+using Volo.Abp.Threading;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpAutoMapperModule),
+ typeof(AbpWebhooksModule),
+ typeof(WebhooksManagementDomainSharedModule))]
+public class WebhooksManagementDomainModule : AbpModule
+{
+ private static readonly OneTimeRunner OneTimeRunner = new();
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAutoMapperObjectMapper();
+
+ Configure(options =>
+ {
+ options.AddProfile(validate: true);
+ });
+
+ Configure(options =>
+ {
+ });
+ }
+
+ public override void PostConfigureServices(ServiceConfigurationContext context)
+ {
+ OneTimeRunner.Run(() =>
+ {
+ // 扩展实体配置
+ //ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity(
+ // WebhooksManagementModuleExtensionConsts.ModuleName,
+ // WebhooksManagementModuleExtensionConsts.EntityNames.Entity,
+ // typeof(Entity)
+ //);
+ });
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj
new file mode 100644
index 000000000..6eb433ff2
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs
new file mode 100644
index 000000000..f6d9c17a8
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Data;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
+
+[ConnectionStringName(WebhooksManagementDbProperties.ConnectionStringName)]
+public interface IWebhooksManagementDbContext : IEfCoreDbContext
+{
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs
new file mode 100644
index 000000000..de73188aa
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Data;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
+
+[ConnectionStringName(WebhooksManagementDbProperties.ConnectionStringName)]
+public class WebhooksManagementDbContext : AbpDbContext, IWebhooksManagementDbContext
+{
+ public WebhooksManagementDbContext(
+ DbContextOptions options) : base(options)
+ {
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ modelBuilder.ConfigureWebhooksManagement();
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs
new file mode 100644
index 000000000..fcf22d3e6
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using Volo.Abp;
+
+namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
+
+public static class WebhooksManagementDbContextModelCreatingExtensions
+{
+ public static void ConfigureWebhooksManagement(
+ this ModelBuilder builder,
+ Action optionsAction = null)
+ {
+ Check.NotNull(builder, nameof(builder));
+
+ var options = new WebhooksManagementModelBuilderConfigurationOptions(
+ WebhooksManagementDbProperties.DbTablePrefix,
+ WebhooksManagementDbProperties.DbSchema
+ );
+ optionsAction?.Invoke(options);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs
new file mode 100644
index 000000000..5751f330f
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs
@@ -0,0 +1,6 @@
+namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
+
+public static class WebhooksManagementEfCoreQueryableExtensions
+{
+ // 在此聚合仓储服务的扩展方法
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs
new file mode 100644
index 000000000..c64b2bb15
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs
@@ -0,0 +1,19 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
+
+[DependsOn(
+ typeof(WebhooksManagementDomainModule),
+ typeof(AbpEntityFrameworkCoreModule))]
+public class WebhooksManagementEntityFrameworkCoreModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAbpDbContext(options =>
+ {
+ options.AddDefaultRepositories();
+ });
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs
new file mode 100644
index 000000000..dc8a2e564
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs
@@ -0,0 +1,17 @@
+using JetBrains.Annotations;
+using Volo.Abp.EntityFrameworkCore.Modeling;
+
+namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore;
+
+public class WebhooksManagementModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
+{
+ public WebhooksManagementModelBuilderConfigurationOptions(
+ [NotNull] string tablePrefix = "",
+ [CanBeNull] string schema = null)
+ : base(
+ tablePrefix,
+ schema)
+ {
+
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj
new file mode 100644
index 000000000..5cee3e037
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs
new file mode 100644
index 000000000..1fff62168
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs
@@ -0,0 +1,18 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpHttpClientModule),
+ typeof(WebhooksManagementApplicationContractsModule))]
+public class WebhooksManagementHttpApiClientModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddHttpClientProxies(
+ typeof(WebhooksManagementApplicationContractsModule).Assembly,
+ WebhooksManagementRemoteServiceConsts.RemoteServiceName);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml
new file mode 100644
index 000000000..be0de3a90
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd
new file mode 100644
index 000000000..11da52550
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj
new file mode 100644
index 000000000..e2556eb0b
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs
new file mode 100644
index 000000000..5eef82130
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementControllerBase.cs
@@ -0,0 +1,12 @@
+using LINGYUN.Abp.WebhooksManagement.Localization;
+using Volo.Abp.AspNetCore.Mvc;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public abstract class WebhooksManagementControllerBase : AbpControllerBase
+{
+ protected WebhooksManagementControllerBase()
+ {
+ LocalizationResource = typeof(WebhooksManagementResource);
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs
new file mode 100644
index 000000000..e4e4699a7
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiModule.cs
@@ -0,0 +1,40 @@
+using LINGYUN.Abp.WebhooksManagement.Localization;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.Validation.Localization;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpAspNetCoreMvcModule),
+ typeof(WebhooksManagementApplicationContractsModule))]
+public class WebhooksManagementHttpApiModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPartIfNotExists(typeof(WebhooksManagementHttpApiModule).Assembly);
+ });
+
+ PreConfigure(options =>
+ {
+ options.AddAssemblyResource(
+ typeof(WebhooksManagementResource),
+ typeof(WebhooksManagementApplicationContractsModule).Assembly);
+ });
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddBaseTypes(typeof(AbpValidationResource));
+ });
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile
index a38f2b0c8..3e9c90691 100644
--- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile
+++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Dockerfile
@@ -7,6 +7,10 @@ COPY . /app
#东8区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone
+## 有些定时作业可能需要建立独立的数据库连接, 可能跨不同的数据库, 配置一下MSSQL
+## 解决连接SqlServer TLS版本过高问题
+RUN sed -i 's/TLSv1.2/TLSv1.0/g' /etc/ssl/openssl.cnf
+RUN sed -i 's/TLSv1.2/TLSv1.0/g' /usr/lib/ssl/openssl.cnf
EXPOSE 80/tcp
VOLUME [ "./app/Logs" ]
diff --git a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj
index 98bc88491..293653249 100644
--- a/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj
+++ b/aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj
@@ -45,6 +45,8 @@
+
+
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs
new file mode 100644
index 000000000..5084440c9
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Controllers/HomeController.cs
@@ -0,0 +1,12 @@
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc;
+
+namespace LY..WebhooksManagement.Controllers;
+
+public class HomeController : AbpController
+{
+ public IActionResult Index()
+ {
+ return Redirect("/swagger/index.html");
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs
new file mode 100644
index 000000000..77ffbf33f
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/DataSeeder/WebhooksManagementDataSeederWorker.cs
@@ -0,0 +1,21 @@
+using Microsoft.Extensions.Hosting;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+
+namespace LY..WebhooksManagement.DataSeeder;
+
+public class WebhooksManagementDataSeederWorker : BackgroundService
+{
+ protected IDataSeeder DataSeeder { get; }
+
+ public WebhooksManagementDataSeederWorker(IDataSeeder dataSeeder)
+ {
+ DataSeeder = dataSeeder;
+ }
+
+ protected async override Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ await DataSeeder.SeedAsync();
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile
new file mode 100644
index 000000000..0d02e98ca
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Dockerfile
@@ -0,0 +1,15 @@
+FROM mcr.microsoft.com/dotnet/aspnet:6.0
+LABEL maintainer="colin.in@foxmail.com"
+WORKDIR /app
+
+COPY . /app
+
+#�Ϻ�ʱ��
+#ENV TZ=Asia/Shanghai
+#RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone
+
+EXPOSE 80/tcp
+VOLUME [ "./app/Logs" ]
+VOLUME [ "./app/Modules" ]
+
+ENTRYPOINT ["dotnet", "LY..WebhooksManagement.HttpApi.Host.dll"]
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs
new file mode 100644
index 000000000..61e3ae739
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContext.cs
@@ -0,0 +1,20 @@
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace LY..WebhooksManagement.EntityFrameworkCore;
+
+public class WebhooksManagementMigrationsDbContext : AbpDbContext
+{
+ public WebhooksManagementMigrationsDbContext(DbContextOptions options)
+ : base(options)
+ {
+
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ modelBuilder.ConfigureWebhooksManagement();
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs
new file mode 100644
index 000000000..cd4432128
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EntityFrameworkCore/WebhooksManagementMigrationsDbContextFactory.cs
@@ -0,0 +1,32 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.Extensions.Configuration;
+using System.IO;
+
+namespace LY..WebhooksManagement.EntityFrameworkCore;
+
+public class WebhooksManagementMigrationsDbContextFactory : IDesignTimeDbContextFactory
+{
+ public WebhooksManagementMigrationsDbContext CreateDbContext(string[] args)
+ {
+ var configuration = BuildConfiguration();
+ var connectionString = configuration.GetConnectionString("WebhooksManagement");
+
+ DbContextOptionsBuilder builder = null;
+
+ builder = new DbContextOptionsBuilder()
+ .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
+
+ return new WebhooksManagementMigrationsDbContext(builder.Options);
+ }
+
+ private static IConfigurationRoot BuildConfiguration()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: false)
+ .AddJsonFile("appsettings.Development.json", optional: true);
+
+ return builder.Build();
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs
new file mode 100644
index 000000000..ccdd31c65
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs
@@ -0,0 +1,69 @@
+using LY..WebhooksManagement.EntityFrameworkCore;
+using LINGYUN.Abp.Data.DbMigrator;
+using LINGYUN.Abp.MultiTenancy;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Uow;
+
+namespace LY..WebhooksManagement.EventBus.Handlers;
+
+public class TenantSynchronizer :
+ IDistributedEventHandler,
+ ITransientDependency
+{
+ protected IDataSeeder DataSeeder { get; }
+ protected ICurrentTenant CurrentTenant { get; }
+ protected IDbSchemaMigrator DbSchemaMigrator { get; }
+ protected IUnitOfWorkManager UnitOfWorkManager { get; }
+
+ protected ILogger Logger { get; }
+
+ public TenantSynchronizer(
+ IDataSeeder dataSeeder,
+ ICurrentTenant currentTenant,
+ IDbSchemaMigrator dbSchemaMigrator,
+ IUnitOfWorkManager unitOfWorkManager,
+ ILogger logger)
+ {
+ DataSeeder = dataSeeder;
+ CurrentTenant = currentTenant;
+ DbSchemaMigrator = dbSchemaMigrator;
+ UnitOfWorkManager = unitOfWorkManager;
+
+ Logger = logger;
+ }
+
+ ///
+ /// 租户创建之后需要预置种子数据
+ ///
+ ///
+ ///
+ public virtual async Task HandleEventAsync(CreateEventData eventData)
+ {
+ using (var unitOfWork = UnitOfWorkManager.Begin())
+ {
+ using (CurrentTenant.Change(eventData.Id, eventData.Name))
+ {
+ Logger.LogInformation("Migrating the new tenant database with LY..WebhooksManagement...");
+ // 迁移租户数据
+ await DbSchemaMigrator.MigrateAsync(
+ (connectionString, builder) =>
+ {
+ builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
+
+ return new WebhooksManagementDbContext(builder.Options);
+ });
+ Logger.LogInformation("Migrated the new tenant database with LY..WebhooksManagement...");
+
+ await DataSeeder.SeedAsync(new DataSeedContext(eventData.Id));
+
+ await unitOfWork.SaveChangesAsync();
+ }
+ }
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj
new file mode 100644
index 000000000..b108df60e
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/LY.MicroService.WebhooksManagement.HttpApi.Host.csproj
@@ -0,0 +1,66 @@
+
+
+
+ net6.0
+ LY..WebhooksManagement
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs
new file mode 100644
index 000000000..f55da1841
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Program.cs
@@ -0,0 +1,58 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Serilog;
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.IO;
+using Volo.Abp.Modularity.PlugIns;
+
+namespace LY..WebhooksManagement;
+
+public class Program
+{
+ public async static Task Main(string[] args)
+ {
+ try
+ {
+ Log.Information("Starting web host.");
+
+ var builder = WebApplication.CreateBuilder(args);
+ builder.Host.AddAppSettingsSecretsJson()
+ .UseAutofac()
+ .ConfigureAppConfiguration((context, config) =>
+ {
+ var configuration = config.Build();
+ if (configuration.GetSection("AgileConfig").Exists())
+ {
+ config.AddAgileConfig(new AgileConfig.Client.ConfigClient(configuration));
+ }
+ })
+ .UseSerilog((context, provider, config) =>
+ {
+ config.ReadFrom.Configuration(context.Configuration);
+ });
+ await builder.AddApplicationAsync(options =>
+ {
+ // 搜索 Modules 目录下所有文件作为插件
+ // 取消显示引用所有其他项目的模块,改为通过插件的形式引用
+ var pluginFolder = Path.Combine(
+ Directory.GetCurrentDirectory(), "Modules");
+ DirectoryHelper.CreateIfNotExists(pluginFolder);
+ options.PlugInSources.AddFolder(
+ pluginFolder,
+ SearchOption.AllDirectories);
+ });
+ var app = builder.Build();
+ await app.InitializeApplicationAsync();
+ await app.RunAsync();
+ return 0;
+ }
+ finally
+ {
+ Log.CloseAndFlush();
+ }
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json
new file mode 100644
index 000000000..6a691ee60
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:20890",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "LY..WebhooksManagement.HttpApi.Host": {
+ "commandName": "Project",
+ "launchBrowser": false,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:57264",
+ "dotnetRunMessages": "true"
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": false,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs
new file mode 100644
index 000000000..e793c0e5c
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/TenantHeaderParamter.cs
@@ -0,0 +1,31 @@
+using Microsoft.Extensions.Options;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+using System.Collections.Generic;
+using Volo.Abp.MultiTenancy;
+
+namespace LY..WebhooksManagement;
+
+public class TenantHeaderParamter : IOperationFilter
+{
+ private readonly AbpMultiTenancyOptions _options;
+ public TenantHeaderParamter(
+ IOptions options)
+ {
+ _options = options.Value;
+ }
+ public void Apply(OpenApiOperation operation, OperationFilterContext context)
+ {
+ if (_options.IsEnabled)
+ {
+ operation.Parameters = operation.Parameters ?? new List();
+ operation.Parameters.Add(new OpenApiParameter
+ {
+ Name = TenantResolverConsts.DefaultTenantKey,
+ In = ParameterLocation.Header,
+ Description = "Tenant Id/Name",
+ Required = false
+ });
+ }
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs
new file mode 100644
index 000000000..95705fa29
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs
@@ -0,0 +1,307 @@
+using DotNetCore.CAP;
+using LINGYUN.Abp.Dapr.Client.DynamicProxying;
+using LINGYUN.Abp.ExceptionHandling;
+using LINGYUN.Abp.ExceptionHandling.Emailing;
+using LINGYUN.Abp.Serilog.Enrichers.Application;
+using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
+using LINGYUN.Abp.Wrapper;
+using Medallion.Threading;
+using Medallion.Threading.Redis;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.DataProtection;
+using Microsoft.Extensions.Caching.StackExchangeRedis;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.OpenApi.Models;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+using StackExchange.Redis;
+using System;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+using Volo.Abp;
+using Volo.Abp.Auditing;
+using Volo.Abp.Caching;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.GlobalFeatures;
+using Volo.Abp.Json;
+using Volo.Abp.Json.SystemTextJson;
+using Volo.Abp.Localization;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Threading;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LY..WebhooksManagement;
+
+public partial class WebhooksManagementHttpApiHostModule
+{
+ protected const string ApplicationName = "WebhooksManagement";
+ private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
+
+ private void PreConfigureFeature()
+ {
+ OneTimeRunner.Run(() =>
+ {
+ GlobalFeatureManager.Instance.Modules.Editions().EnableAll();
+ });
+ }
+
+ private void PreConfigureApp()
+ {
+ AbpSerilogEnrichersConsts.ApplicationName = ApplicationName;
+
+ PreConfigure(options =>
+ {
+ // 以开放端口区别
+ options.SnowflakeIdOptions.WorkerId = 5000;
+ options.SnowflakeIdOptions.WorkerIdBits = 5;
+ options.SnowflakeIdOptions.DatacenterId = 1;
+ });
+ }
+
+ private void PreConfigureCAP(IConfiguration configuration)
+ {
+ PreConfigure(options =>
+ {
+ options
+ .UseMySql(sqlOptions =>
+ {
+ configuration.GetSection("CAP:MySql").Bind(sqlOptions);
+ })
+ .UseRabbitMQ(rabbitMQOptions =>
+ {
+ configuration.GetSection("CAP:RabbitMQ").Bind(rabbitMQOptions);
+ })
+ .UseDashboard();
+ });
+ }
+
+ private void ConfigureDbContext()
+ {
+ // 配置Ef
+ Configure(options =>
+ {
+ options.UseMySQL();
+ });
+ }
+
+ private void ConfigureJsonSerializer()
+ {
+ // 解决某些不支持类型的序列化
+ Configure(options =>
+ {
+ options.DefaultDateTimeFormat = "yyyy-MM-dd HH:mm:ss";
+ });
+ // 中文序列化的编码问题
+ Configure(options =>
+ {
+ options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
+ });
+ }
+
+ private void ConfigureDistributedLock(IServiceCollection services, IConfiguration configuration)
+ {
+ if (configuration.GetSection("DistributedLock").Exists())
+ {
+ var redis = ConnectionMultiplexer.Connect(configuration["DistributedLock:Redis:Configuration"]);
+ services.AddSingleton(_ => new RedisDistributedSynchronizationProvider(redis.GetDatabase()));
+ }
+ }
+
+ private void ConfigureOpenTelemetry(IServiceCollection services, IConfiguration configuration)
+ {
+ if (configuration.GetSection("OpenTelemetry").Exists())
+ {
+ services.AddOpenTelemetryTracing(cfg =>
+ {
+ cfg.AddSource(ApplicationName)
+ .SetResourceBuilder(
+ ResourceBuilder.CreateDefault().AddService(ApplicationName))
+ .AddHttpClientInstrumentation()
+ .AddAspNetCoreInstrumentation()
+ .AddEntityFrameworkCoreInstrumentation()
+ .AddCapInstrumentation()
+ .AddZipkinExporter(zipKinOptions =>
+ {
+ var endpoint = configuration["OpenTelemetry:ZipKin:Endpoint"];
+ if (!endpoint.IsNullOrWhiteSpace())
+ {
+ zipKinOptions.Endpoint = new Uri(configuration["OpenTelemetry:ZipKin:Endpoint"]);
+ }
+ });
+ });
+ }
+ }
+
+ private void ConfigureExceptionHandling()
+ {
+ // 自定义需要处理的异常
+ Configure(options =>
+ {
+ // 加入需要处理的异常类型
+ options.Handlers.Add();
+ options.Handlers.Add();
+ options.Handlers.Add();
+ options.Handlers.Add();
+ options.Handlers.Add();
+ options.Handlers.Add();
+ });
+ // 自定义需要发送邮件通知的异常类型
+ Configure(options =>
+ {
+ // 是否发送堆栈信息
+ options.SendStackTrace = true;
+ // 未指定异常接收者的默认接收邮件
+ // 指定自己的邮件地址
+ });
+ }
+
+ private void ConfigureAuditing(IConfiguration configuration)
+ {
+ Configure(options =>
+ {
+ options.ApplicationName = "WebhooksManagement";
+ // 是否启用实体变更记录
+ var entitiesChangedConfig = configuration.GetSection("App:TrackingEntitiesChanged");
+ if (entitiesChangedConfig.Exists() && entitiesChangedConfig.Get())
+ {
+ options
+ .EntityHistorySelectors
+ .AddAllEntities();
+ }
+ });
+ }
+
+ private void ConfigureCaching(IConfiguration configuration)
+ {
+ Configure(options =>
+ {
+ // 最好统一命名,不然某个缓存变动其他应用服务有例外发生
+ options.KeyPrefix = "LINGYUN.Abp.Application";
+ // 滑动过期30天
+ options.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromDays(30d);
+ // 绝对过期60天
+ options.GlobalCacheEntryOptions.AbsoluteExpiration = DateTimeOffset.Now.AddDays(60d);
+ });
+
+ Configure(options =>
+ {
+ var redisConfig = ConfigurationOptions.Parse(options.Configuration);
+ options.ConfigurationOptions = redisConfig;
+ options.InstanceName = configuration["Redis:InstanceName"];
+ });
+ }
+
+ private void ConfigureVirtualFileSystem()
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded("LY..WebhooksManagement");
+ });
+ }
+
+ private void ConfigureMultiTenancy(IConfiguration configuration)
+ {
+ // 多租户
+ Configure(options =>
+ {
+ options.IsEnabled = true;
+ });
+
+ var tenantResolveCfg = configuration.GetSection("App:Domains");
+ if (tenantResolveCfg.Exists())
+ {
+ Configure(options =>
+ {
+ var domains = tenantResolveCfg.Get();
+ foreach (var domain in domains)
+ {
+ options.AddDomainTenantResolver(domain);
+ }
+ });
+ }
+ }
+
+ private void ConfigureSwagger(IServiceCollection services)
+ {
+ // Swagger
+ services.AddSwaggerGen(
+ options =>
+ {
+ options.SwaggerDoc("v1", new OpenApiInfo { Title = "WebhooksManagement API", Version = "v1" });
+ options.DocInclusionPredicate((docName, description) => true);
+ options.CustomSchemaIds(type => type.FullName);
+ options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
+ {
+ Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
+ Name = "Authorization",
+ In = ParameterLocation.Header,
+ Scheme = "bearer",
+ Type = SecuritySchemeType.Http,
+ BearerFormat = "JWT"
+ });
+ options.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
+ },
+ new string[] { }
+ }
+ });
+ options.OperationFilter();
+ });
+ }
+
+ private void ConfigureLocalization()
+ {
+ // 支持本地化语言类型
+ Configure(options =>
+ {
+ options.Languages.Add(new LanguageInfo("en", "en", "English"));
+ options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
+ // 动态语言支持
+ options.Resources.AddDynamic();
+ });
+ }
+
+ private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false)
+ {
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ .AddJwtBearer(options =>
+ {
+ options.Authority = configuration["AuthServer:Authority"];
+ options.RequireHttpsMetadata = false;
+ options.Audience = configuration["AuthServer:ApiName"];
+ });
+
+ if (!isDevelopment)
+ {
+ var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
+ services
+ .AddDataProtection()
+ .SetApplicationName("LINGYUN.Abp.Application")
+ .PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys");
+ }
+ }
+
+ private void ConfigureWrapper()
+ {
+ Configure(options =>
+ {
+ // 取消注释包装结果
+ // options.IsEnabled = true;
+ });
+
+ Configure(options =>
+ {
+ // dapr服务间调用发送不需要包装结果的请求头
+ options.ProxyRequestActions.Add(
+ (appId, httpRequestMessage) =>
+ {
+ httpRequestMessage.Headers.TryAddWithoutValidation(AbpHttpWrapConsts.AbpDontWrapResult, "true");
+ });
+ });
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs
new file mode 100644
index 000000000..8c2982179
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.DataSeeder.cs
@@ -0,0 +1,15 @@
+using Microsoft.Extensions.DependencyInjection;
+using LY..WebhooksManagement.DataSeeder;
+
+namespace LY..WebhooksManagement;
+
+public partial class WebhooksManagementHttpApiHostModule
+{
+ private static void ConfigureSeedWorker(IServiceCollection services, bool isDevelopment = false)
+ {
+ if (isDevelopment)
+ {
+ services.AddHostedService();
+ }
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs
new file mode 100644
index 000000000..4dac2cea9
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.cs
@@ -0,0 +1,118 @@
+using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
+using LINGYUN.Abp.AuditLogging.Elasticsearch;
+using LINGYUN.Abp.EventBus.CAP;
+using LINGYUN.Abp.ExceptionHandling.Emailing;
+using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore;
+using LINGYUN.Abp.Saas.EntityFrameworkCore;
+using LINGYUN.Abp.Serilog.Enrichers.Application;
+using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using LY..WebhooksManagement.EntityFrameworkCore;
+using LY..WebhooksManagement.SettingManagement;
+using Volo.Abp;
+using Volo.Abp.AspNetCore.Authentication.JwtBearer;
+using Volo.Abp.AspNetCore.MultiTenancy;
+using Volo.Abp.AspNetCore.Serilog;
+using Volo.Abp.Autofac;
+using Volo.Abp.Caching.StackExchangeRedis;
+using Volo.Abp.DistributedLocking;
+using Volo.Abp.EntityFrameworkCore.MySQL;
+using Volo.Abp.FeatureManagement.EntityFrameworkCore;
+using Volo.Abp.Http.Client.IdentityModel.Web;
+using Volo.Abp.Modularity;
+using Volo.Abp.PermissionManagement.EntityFrameworkCore;
+using Volo.Abp.SettingManagement.EntityFrameworkCore;
+using Volo.Abp.Swashbuckle;
+
+namespace LY.WebhooksManagement;
+
+[DependsOn(
+ typeof(AbpSerilogEnrichersApplicationModule),
+ typeof(AbpSerilogEnrichersUniqueIdModule),
+ typeof(AbpAuditLoggingElasticsearchModule),
+ typeof(AbpAspNetCoreSerilogModule),
+ typeof(WebhooksManagementApplicationModule),
+ typeof(WebhooksManagementHttpApiModule),
+ typeof(WebhooksManagementEntityFrameworkCoreModule),
+ typeof(WebhooksManagementSettingManagementModule),
+ typeof(AbpEntityFrameworkCoreMySQLModule),
+ typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
+ typeof(AbpEmailingExceptionHandlingModule),
+ typeof(AbpCAPEventBusModule),
+ typeof(AbpHttpClientIdentityModelWebModule),
+ typeof(AbpAspNetCoreMultiTenancyModule),
+ typeof(AbpSaasEntityFrameworkCoreModule),
+ typeof(AbpFeatureManagementEntityFrameworkCoreModule),
+ typeof(AbpPermissionManagementEntityFrameworkCoreModule),
+ typeof(AbpSettingManagementEntityFrameworkCoreModule),
+ typeof(AbpLocalizationManagementEntityFrameworkCoreModule),
+ typeof(AbpCachingStackExchangeRedisModule),
+ typeof(AbpDistributedLockingModule),
+ typeof(AbpAspNetCoreMvcWrapperModule),
+ typeof(AbpSwashbuckleModule),
+ typeof(AbpAutofacModule)
+ )]
+public partial class WebhooksManagementHttpApiHostModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ var configuration = context.Services.GetConfiguration();
+
+ PreConfigureApp();
+ PreConfigureFeature();
+ PreConfigureCAP(configuration);
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var hostingEnvironment = context.Services.GetHostingEnvironment();
+ var configuration = context.Services.GetConfiguration();
+
+ ConfigureWrapper();
+ ConfigureDbContext();
+ ConfigureLocalization();
+ ConfigureJsonSerializer();
+ ConfigureExceptionHandling();
+ ConfigureVirtualFileSystem();
+ ConfigureCaching(configuration);
+ ConfigureAuditing(configuration);
+ ConfigureMultiTenancy(configuration);
+ ConfigureSwagger(context.Services);
+ ConfigureOpenTelemetry(context.Services, configuration);
+ ConfigureDistributedLock(context.Services, configuration);
+ ConfigureSeedWorker(context.Services, hostingEnvironment.IsDevelopment());
+ ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment());
+ }
+
+ public override void OnApplicationInitialization(ApplicationInitializationContext context)
+ {
+ var app = context.GetApplicationBuilder();
+ var env = context.GetEnvironment();
+
+ app.UseCorrelationId();
+ app.UseStaticFiles();
+ app.UseRouting();
+ app.UseCors();
+ app.UseAuthentication();
+ app.UseJwtTokenMiddleware();
+ app.UseMultiTenancy();
+ app.UseMapRequestLocalization();
+ app.UseAuthorization();
+ app.UseSwagger();
+ app.UseAbpSwaggerUI(options =>
+ {
+ options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API");
+
+ var configuration = context.GetConfiguration();
+ options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
+ options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
+ options.OAuthScopes("WebhooksManagement");
+ });
+ app.UseAuditing();
+ app.UseAbpSerilogEnrichers();
+ app.UseConfiguredEndpoints();
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json
new file mode 100644
index 000000000..7c28c17fe
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.Development.json
@@ -0,0 +1,132 @@
+{
+ "AgileConfig": {
+ "env": "DEV",
+ "appId": "LY.MicroService.WebhooksManagement",
+ "secret": "1q2w3E*",
+ "nodes": "http://127.0.0.1:15000",
+ "name": "LY.MicroService.WebhooksManagement",
+ "tag": "LY.MicroService.WebhooksManagement"
+ },
+ "App": {
+ "TrackingEntitiesChanged": true
+ },
+ "ConnectionStrings": {
+ "Default": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456",
+ "WebhooksManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456",
+ "AbpSaas": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456",
+ "AbpFeatureManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456",
+ "AbpPermissionManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456",
+ "AbpSettingManagement": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456"
+ },
+ "DistributedLock": {
+ "Redis": {
+ "Configuration": "127.0.0.1,defaultDatabase=15"
+ }
+ },
+ "OpenTelemetry": {
+ "ZipKin": {
+ "Endpoint": "http://127.0.0.1:9411/api/v2/spans"
+ }
+ },
+ "RemoteServices": {},
+ "IdentityClients": {
+ "InternalServiceClient": {
+ "Authority": "http://127.0.0.1:44385",
+ "RequireHttps": false,
+ "GrantType": "client_credentials",
+ "Scope": "lingyun-abp-application",
+ "ClientId": "InternalServiceClient",
+ "ClientSecret": "1q2w3E*"
+ }
+ },
+ "CAP": {
+ "EventBus": {
+ "DefaultGroupName": "WebhooksManagement",
+ "GroupNamePrefix": "Dev",
+ "Version": "v1",
+ "FailedRetryInterval": 300,
+ "FailedRetryCount": 10
+ },
+ "MySql": {
+ "TableNamePrefix": "WebhooksManagement_cap",
+ "ConnectionString": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456"
+ },
+ "SqlServer": {
+ "TableNamePrefix": "WebhooksManagement_cap",
+ "ConnectionString": "Server=127.0.0.1;Database=WebhooksManagement;User Id=root;Password=123456"
+ },
+ "Sqlite": {
+ "TableNamePrefix": "WebhooksManagement_cap",
+ "ConnectionString": "Data Source=./cap.db"
+ },
+ "Oracle": {
+ "TableNamePrefix": "WebhooksManagement_cap",
+ "ConnectionString": "Data Source=WebhooksManagement;Integrated Security=yes;"
+ },
+ "PostgreSql": {
+ "TableNamePrefix": "WebhooksManagement_cap",
+ "ConnectionString": "Host=localhost;Port=5432;Database=WebhooksManagement;User ID=root;Password=123456;"
+ },
+ "RabbitMQ": {
+ "HostName": "127.0.0.1",
+ "Port": 5672,
+ "UserName": "admin",
+ "Password": "123456",
+ "ExchangeName": "LINGYUN.Abp.Application",
+ "VirtualHost": "/"
+ }
+ },
+ "Redis": {
+ "Configuration": "127.0.0.1,defaultDatabase=10",
+ "InstanceName": "LINGYUN.Abp.Application"
+ },
+ "AuthServer": {
+ "Authority": "http://127.0.0.1:44385/",
+ "ApiName": "lingyun-abp-application",
+ "SwaggerClientId": "InternalServiceClient",
+ "SwaggerClientSecret": "1q2w3E*"
+ },
+ "Logging": {
+ "Serilog": {
+ "Elasticsearch": {
+ "IndexFormat": "abp.dev.logging-{0:yyyy.MM.dd}"
+ }
+ }
+ },
+ "AuditLogging": {
+ "Elasticsearch": {
+ "IndexPrefix": "abp.dev.auditing"
+ }
+ },
+ "Elasticsearch": {
+ "NodeUris": "http://127.0.0.1:9200"
+ },
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "System": "Warning",
+ "Microsoft": "Warning",
+ "DotNetCore": "Debug"
+ }
+ },
+ "WriteTo": [
+ {
+ "Name": "Console",
+ "Args": {
+ "restrictedToMinimumLevel": "Debug",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "Elasticsearch",
+ "Args": {
+ "nodeUris": "http://127.0.0.1:9200",
+ "indexFormat": "abp.dev.logging-{0:yyyy.MM.dd}",
+ "autoRegisterTemplate": true,
+ "autoRegisterTemplateVersion": "ESv7"
+ }
+ }
+ ]
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json
new file mode 100644
index 000000000..67e1bc4bd
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/appsettings.json
@@ -0,0 +1,80 @@
+{
+ "StringEncryption": {
+ "DefaultPassPhrase": "s46c5q55nxpeS8Ra",
+ "InitVectorBytes": "s83ng0abvd02js84",
+ "DefaultSalt": "sf&5)s3#"
+ },
+ "AllowedHosts": "*",
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "Microsoft.EntityFrameworkCore": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ },
+ "Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId", "WithEnvironmentName", "WithMachineName", "WithApplicationName", "WithUniqueId" ],
+ "WriteTo": [
+ {
+ "Name": "Console",
+ "Args": {
+ "initialMinimumLevel": "Verbose",
+ "standardErrorFromLevel": "Verbose",
+ "restrictedToMinimumLevel": "Verbose",
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Debug-.log",
+ "restrictedToMinimumLevel": "Debug",
+ "rollingInterval": "Day",
+ "fileSizeLimitBytes": 5242880,
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Info-.log",
+ "restrictedToMinimumLevel": "Information",
+ "rollingInterval": "Day",
+ "fileSizeLimitBytes": 5242880,
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Warn-.log",
+ "restrictedToMinimumLevel": "Warning",
+ "rollingInterval": "Day",
+ "fileSizeLimitBytes": 5242880,
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Error-.log",
+ "restrictedToMinimumLevel": "Error",
+ "rollingInterval": "Day",
+ "fileSizeLimitBytes": 5242880,
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/Fatal-.log",
+ "restrictedToMinimumLevel": "Fatal",
+ "rollingInterval": "Day",
+ "fileSizeLimitBytes": 5242880,
+ "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
+ }
+ }
+ ]
+ }
+}
diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh
new file mode 100644
index 000000000..ddfd59d01
--- /dev/null
+++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/dapr.sh
@@ -0,0 +1 @@
+dapr run --app-id WebhooksManagement --app-port 57264 -H 63751 -- dotnet run --no-build
\ No newline at end of file
From 5d525ea3ba2bce9889368c06c2ea4388fccdaa7a Mon Sep 17 00:00:00 2001
From: cKey <35512826+colinin@users.noreply.github.com>
Date: Sat, 26 Mar 2022 10:59:40 +0800
Subject: [PATCH 02/17] feat(webhooks): added data access configuration
---
aspnet-core/Directory.Build.props | 5 +
.../Abp/Webhooks/DefaultWebhookPublisher.cs | 9 +-
.../WebhookSubscriptionExtensions.cs | 21 +++
.../Webhooks/NullWebhookSendAttemptStore.cs | 4 +-
.../LINGYUN/Abp/Webhooks/WebhookManager.cs | 12 +-
.../Webhooks/WebhookSubscriptionManager.cs | 6 +-
.../Localization/Resources/en.json | 4 +-
.../Localization/Resources/zh-Hans.json | 4 +-
...sManagementModuleExtensionConfiguration.cs | 8 +-
...ensionConfigurationDictionaryExtensions.cs | 2 -
...WebhooksManagementModuleExtensionConsts.cs | 4 +-
.../Abp/WebhooksManagement/WebhookEventEto.cs | 12 ++
.../WebhookEventRecordConsts.cs | 7 +
.../WebhookSendAttemptEto.cs | 16 ++
.../WebhookSendRecordConsts.cs | 6 +
.../WebhookSubscriptionConsts.cs | 9 +
.../WebhookSubscriptionEto.cs | 14 ++
.../DefaultWebhookManager.cs | 4 +-
.../WebhookSubscriptionExtensions.cs | 29 +++
.../WebhookSubscriptionInfoExtensions.cs | 175 ++++++++++++++++++
.../IWebhookSendRecordRepository.cs | 1 +
.../WebhooksManagement/WebhookEventRecord.cs | 5 +-
.../WebhooksManagement/WebhookEventStore.cs | 9 +-
.../WebhookSendAttemptStore.cs | 127 +++++--------
.../WebhooksManagement/WebhookSendRecord.cs | 3 +-
.../WebhookSendRecordFilter.cs | 2 -
.../WebhooksManagement/WebhookSubscription.cs | 24 ++-
.../WebhookSubscriptionsStore.cs | 129 +++++++++++--
.../WebhooksManagementDomainMapperProfile.cs | 9 +
.../WebhooksManagementDomainModule.cs | 29 ++-
.../EfCoreWebhookEventRecordRepository.cs | 15 ++
.../EfCoreWebhookSendRecordRepository.cs | 58 ++++++
.../EfCoreWebhookSubscriptionRepository.cs | 16 ++
...agementDbContextModelCreatingExtensions.cs | 55 ++++++
...ooksManagementEfCoreQueryableExtensions.cs | 15 +-
...ooksManagementEntityFrameworkCoreModule.cs | 4 +
.../Controllers/HomeController.cs | 2 +-
.../WebhooksManagementDataSeederWorker.cs | 2 +-
.../WebhooksManagementMigrationsDbContext.cs | 5 +-
...oksManagementMigrationsDbContextFactory.cs | 2 +-
.../EventBus/Handlers/TenantSynchronizer.cs | 10 +-
...ice.WebhooksManagement.HttpApi.Host.csproj | 9 +-
.../Program.cs | 2 +-
.../Properties/launchSettings.json | 9 +-
.../TenantHeaderParamter.cs | 2 +-
...ksManagementHttpApiHostModule.Configure.cs | 8 +-
...sManagementHttpApiHostModule.DataSeeder.cs | 4 +-
.../WebhooksManagementHttpApiHostModule.cs | 11 +-
48 files changed, 722 insertions(+), 196 deletions(-)
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/Extensions/WebhookSubscriptionExtensions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventEto.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventRecordConsts.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptEto.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordConsts.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionConsts.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionEto.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionExtensions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionInfoExtensions.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/EfCoreWebhookEventRecordRepository.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/EfCoreWebhookSendRecordRepository.cs
create mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/EfCoreWebhookSubscriptionRepository.cs
diff --git a/aspnet-core/Directory.Build.props b/aspnet-core/Directory.Build.props
index 969253fd5..9f9403ebe 100644
--- a/aspnet-core/Directory.Build.props
+++ b/aspnet-core/Directory.Build.props
@@ -12,6 +12,11 @@
2.0.3
1.7.28
7.15.1
+ 1.0.0-rc8
+ 1.2.0-rc1
+ 1.0.0-rc8
+ 1.0.0-rc8
+ 1.0.0-beta2
3.3.3
2.0.593
2.10.0
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
index d466ee0f8..5b2d38c71 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
@@ -1,10 +1,10 @@
-using System;
+using Newtonsoft.Json;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Guids;
-using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Webhooks
@@ -15,7 +15,6 @@ namespace LINGYUN.Abp.Webhooks
private readonly ICurrentTenant _currentTenant;
private readonly IGuidGenerator _guidGenerator;
- private readonly IJsonSerializer _jsonSerializer;
private readonly IBackgroundJobManager _backgroundJobManager;
private readonly IWebhookSubscriptionManager _webhookSubscriptionManager;
@@ -23,12 +22,10 @@ namespace LINGYUN.Abp.Webhooks
IWebhookSubscriptionManager webhookSubscriptionManager,
ICurrentTenant currentTenant,
IGuidGenerator guidGenerator,
- IJsonSerializer jsonSerializer,
IBackgroundJobManager backgroundJobManager)
{
_currentTenant = currentTenant;
_guidGenerator = guidGenerator;
- _jsonSerializer = jsonSerializer;
_backgroundJobManager = backgroundJobManager;
_webhookSubscriptionManager = webhookSubscriptionManager;
@@ -114,7 +111,7 @@ namespace LINGYUN.Abp.Webhooks
{
Id = _guidGenerator.Create(),
WebhookName = webhookName,
- Data = _jsonSerializer.Serialize(data),
+ Data = JsonConvert.SerializeObject(data),
TenantId = tenantId
};
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/Extensions/WebhookSubscriptionExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/Extensions/WebhookSubscriptionExtensions.cs
new file mode 100644
index 000000000..8cf48f520
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/Extensions/WebhookSubscriptionExtensions.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Webhooks.Extensions
+{
+ public static class WebhookSubscriptionExtensions
+ {
+ ///
+ /// checks if subscribed to given webhook
+ ///
+ ///
+ public static bool IsSubscribed(this WebhookSubscriptionInfo webhookSubscription, string webhookName)
+ {
+ if (webhookSubscription.Webhooks.IsNullOrEmpty())
+ {
+ return false;
+ }
+
+ return webhookSubscription.Webhooks.Contains(webhookName);
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
index 75749401b..7254b1ae6 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
@@ -33,10 +33,10 @@ namespace LINGYUN.Abp.Webhooks
return default;
}
- public Task> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount,
+ public Task<(int TotalCount, IReadOnlyCollection Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount,
int skipCount)
{
- return Task.FromResult(new List() as IReadOnlyCollection);
+ return Task.FromResult(ValueTuple.Create(0, new List() as IReadOnlyCollection));
}
public Task> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId)
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
index 29389e15e..854795786 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
@@ -1,10 +1,10 @@
-using System;
+using Newtonsoft.Json;
+using System;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
-using Volo.Abp.Json;
namespace LINGYUN.Abp.Webhooks
{
@@ -13,21 +13,17 @@ namespace LINGYUN.Abp.Webhooks
private const string SignatureHeaderKey = "sha256";
private const string SignatureHeaderValueTemplate = SignatureHeaderKey + "={0}";
private const string SignatureHeaderName = "abp-webhook-signature";
-
- protected IJsonSerializer JsonSerializer { get; }
protected IWebhookSendAttemptStore WebhookSendAttemptStore { get; }
protected WebhookManager(
- IJsonSerializer jsonSerializer,
IWebhookSendAttemptStore webhookSendAttemptStore)
{
- JsonSerializer = jsonSerializer;
WebhookSendAttemptStore = webhookSendAttemptStore;
}
public virtual async Task GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs)
{
- var data = JsonSerializer.Serialize(webhookSenderArgs.Data);
+ var data = JsonConvert.SerializeObject(webhookSenderArgs.Data);
var attemptNumber = await WebhookSendAttemptStore.GetSendAttemptCountAsync(
webhookSenderArgs.TenantId,
@@ -72,7 +68,7 @@ namespace LINGYUN.Abp.Webhooks
var payload = await GetWebhookPayloadAsync(webhookSenderArgs);
- var serializedBody = JsonSerializer.Serialize(payload);
+ var serializedBody = JsonConvert.SerializeObject(payload);
return serializedBody;
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
index 4ed53d52b..9db42f506 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
@@ -93,11 +93,7 @@ namespace LINGYUN.Abp.Webhooks
}
else
{
- var subscription = await WebhookSubscriptionsStore.GetAsync(webhookSubscription.Id);
- subscription.WebhookUri = webhookSubscription.WebhookUri;
- subscription.Webhooks = webhookSubscription.Webhooks;
- subscription.Headers = webhookSubscription.Headers;
- await WebhookSubscriptionsStore.UpdateAsync(subscription);
+ await WebhookSubscriptionsStore.UpdateAsync(webhookSubscription);
}
await uow.SaveChangesAsync();
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json
index f31721006..653188d9b 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json
@@ -1,8 +1,8 @@
{
"culture": "en",
"texts": {
- "Features:WebhooksManagement": "WebhooksManagement",
- "Permission:WebhooksManagement": "WebhooksManagement",
+ "Features:WebhooksManagement": "Webhooks",
+ "Permission:WebhooksManagement": "Webhooks",
"Permission:ManageSettings": "Manage Settings"
}
}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json
index 759c3f745..bfcd4d2a2 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json
@@ -1,8 +1,8 @@
{
"culture": "zh-Hans",
"texts": {
- "Features:WebhooksManagement": "WebhooksManagement",
- "Permission:WebhooksManagement": "WebhooksManagement",
+ "Features:WebhooksManagement": "Webhooks",
+ "Permission:WebhooksManagement": "Webhooks",
"Permission:ManageSettings": "管理设置"
}
}
\ No newline at end of file
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs
index 9b810dd32..bf7e418ce 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs
@@ -8,9 +8,9 @@ public class WebhooksManagementModuleExtensionConfiguration : ModuleExtensionCon
public WebhooksManagementModuleExtensionConfiguration ConfigureWebhooksManagement(
Action configureAction)
{
- return this.ConfigureEntity(
- WebhooksManagementModuleExtensionConsts.EntityNames.Entity,
- configureAction
- );
+ return this
+ .ConfigureEntity(WebhooksManagementModuleExtensionConsts.EntityNames.WebhookEvent, configureAction)
+ .ConfigureEntity(WebhooksManagementModuleExtensionConsts.EntityNames.WebhookSendAttempt, configureAction)
+ .ConfigureEntity(WebhooksManagementModuleExtensionConsts.EntityNames.WebhookSubscription, configureAction);
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs
index b41e10ccb..fe0bff94b 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Text;
using Volo.Abp.ObjectExtending.Modularity;
namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending;
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs
index b16fe772e..a51d51035 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs
@@ -6,6 +6,8 @@ public static class WebhooksManagementModuleExtensionConsts
public static class EntityNames
{
- public const string Entity = "Entity";
+ public const string WebhookEvent = "WebhookEvent";
+ public const string WebhookSendAttempt = "WebhookSendAttempt";
+ public const string WebhookSubscription = "WebhookSubscription";
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventEto.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventEto.cs
new file mode 100644
index 000000000..f6625a851
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventEto.cs
@@ -0,0 +1,12 @@
+using System;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[Serializable]
+public class WebhookEventEto : IMultiTenant
+{
+ public Guid Id { get; set; }
+ public Guid? TenantId { get; set; }
+ public string WebhookName { get; set; }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventRecordConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventRecordConsts.cs
new file mode 100644
index 000000000..31f4cf431
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookEventRecordConsts.cs
@@ -0,0 +1,7 @@
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public static class WebhookEventRecordConsts
+{
+ public static int MaxWebhookNameLength { get; set; } = 100;
+ public static int MaxDataLength { get; set; } = int.MaxValue;
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptEto.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptEto.cs
new file mode 100644
index 000000000..12f1cb812
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptEto.cs
@@ -0,0 +1,16 @@
+using System;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[Serializable]
+public class WebhookSendAttemptEto : IMultiTenant
+{
+ public Guid Id { get; set; }
+
+ public Guid? TenantId { get; set; }
+
+ public Guid WebhookEventId { get; set; }
+
+ public Guid WebhookSubscriptionId { get; set; }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordConsts.cs
new file mode 100644
index 000000000..464658f04
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordConsts.cs
@@ -0,0 +1,6 @@
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public static class WebhookSendRecordConsts
+{
+ public static int MaxResponseLength { get; set; } = int.MaxValue;
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionConsts.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionConsts.cs
new file mode 100644
index 000000000..70a51fc17
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionConsts.cs
@@ -0,0 +1,9 @@
+namespace LINGYUN.Abp.WebhooksManagement;
+
+public static class WebhookSubscriptionConsts
+{
+ public static int MaxWebhookUriLength { get; set; } = 255;
+ public static int MaxSecretLength { get; set; } = 128;
+ public static int MaxWebhooksLength { get; set; } = int.MaxValue;
+ public static int MaxHeadersLength { get; set; } = int.MaxValue;
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionEto.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionEto.cs
new file mode 100644
index 000000000..157d6bc04
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionEto.cs
@@ -0,0 +1,14 @@
+using System;
+using Volo.Abp.MultiTenancy;
+
+namespace LINGYUN.Abp.WebhooksManagement;
+
+[Serializable]
+public class WebhookSubscriptionEto : IMultiTenant
+{
+ public Guid Id { get; set; }
+
+ public Guid? TenantId { get; set; }
+
+ public string WebhookUri { get; set; }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs
index c2535823c..8b8b56129 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs
@@ -4,7 +4,6 @@ using System.Net;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
-using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
@@ -19,11 +18,10 @@ public class DefaultWebhookManager : WebhookManager, ITransientDependency
public DefaultWebhookManager(
ICurrentTenant currentTenant,
IGuidGenerator guidGenerator,
- IJsonSerializer jsonSerializer,
IWebhookSendAttemptStore webhookSendAttemptStore,
IUnitOfWorkManager unitOfWorkManager,
IWebhookSendRecordRepository webhookSendAttemptRepository)
- : base(jsonSerializer, webhookSendAttemptStore)
+ : base(webhookSendAttemptStore)
{
CurrentTenant = currentTenant;
GuidGenerator = guidGenerator;
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionExtensions.cs
new file mode 100644
index 000000000..3630b3760
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionExtensions.cs
@@ -0,0 +1,29 @@
+using LINGYUN.Abp.Webhooks;
+using Newtonsoft.Json;
+using System.Linq;
+
+namespace LINGYUN.Abp.WebhooksManagement.Extensions
+{
+ public static class WebhookSubscriptionExtensions
+ {
+ public static string ToSubscribedWebhooksString(this WebhookSubscriptionInfo webhookSubscription)
+ {
+ if (webhookSubscription.Webhooks.Any())
+ {
+ return JsonConvert.SerializeObject(webhookSubscription.Webhooks);
+ }
+
+ return null;
+ }
+
+ public static string ToWebhookHeadersString(this WebhookSubscriptionInfo webhookSubscription)
+ {
+ if (webhookSubscription.Headers.Any())
+ {
+ return JsonConvert.SerializeObject(webhookSubscription.Headers);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionInfoExtensions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionInfoExtensions.cs
new file mode 100644
index 000000000..8068dfa10
--- /dev/null
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionInfoExtensions.cs
@@ -0,0 +1,175 @@
+using LINGYUN.Abp.Webhooks;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.WebhooksManagement.Extensions
+{
+ public static class WebhookSubscriptionInfoExtensions
+ {
+ ///
+ /// Return List of subscribed webhooks definitions
+ ///
+ ///
+ public static List GetSubscribedWebhooks(this WebhookSubscription webhookSubscription)
+ {
+ if (webhookSubscription.Webhooks.IsNullOrWhiteSpace())
+ {
+ return new List();
+ }
+
+ return JsonConvert.DeserializeObject>(webhookSubscription.Webhooks);
+ }
+
+ ///
+ /// Adds webhook subscription to if not exists
+ ///
+ ///
+ /// webhook unique name
+ public static void SubscribeWebhook(this WebhookSubscription webhookSubscription, string name)
+ {
+ name = name.Trim();
+ if (name.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(name), $"{nameof(name)} can not be null, empty or whitespace!");
+ }
+
+ var webhookDefinitions = webhookSubscription.GetSubscribedWebhooks();
+ if (webhookDefinitions.Contains(name))
+ {
+ return;
+ }
+
+ webhookDefinitions.Add(name);
+ webhookSubscription.SetWebhooks(JsonConvert.SerializeObject(webhookDefinitions));
+ }
+
+ ///
+ /// Removes webhook subscription from if exists
+ ///
+ ///
+ /// webhook unique name
+ public static void UnsubscribeWebhook(this WebhookSubscription webhookSubscription, string name)
+ {
+ name = name.Trim();
+ if (name.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(name), $"{nameof(name)} can not be null, empty or whitespace!");
+ }
+
+ var webhookDefinitions = webhookSubscription.GetSubscribedWebhooks();
+ if (!webhookDefinitions.Contains(name))
+ {
+ return;
+ }
+
+ webhookDefinitions.Remove(name);
+ webhookSubscription.SetWebhooks(JsonConvert.SerializeObject(webhookDefinitions));
+ }
+
+ ///
+ /// Clears all
+ ///
+ ///
+ public static void RemoveAllSubscribedWebhooks(this WebhookSubscription webhookSubscription)
+ {
+ webhookSubscription.SetWebhooks(null);
+ }
+
+ ///
+ /// if subscribed to given webhook
+ ///
+ ///
+ public static bool IsSubscribed(this WebhookSubscription webhookSubscription, string webhookName)
+ {
+ if (webhookSubscription.Webhooks.IsNullOrWhiteSpace())
+ {
+ return false;
+ }
+
+ return webhookSubscription.GetSubscribedWebhooks().Contains(webhookName);
+ }
+
+ ///
+ /// Returns additional webhook headers
+ ///
+ ///
+ public static IDictionary GetWebhookHeaders(this WebhookSubscription webhookSubscription)
+ {
+ if (webhookSubscription.Headers.IsNullOrWhiteSpace())
+ {
+ return new Dictionary();
+ }
+
+ return JsonConvert.DeserializeObject>(webhookSubscription.Headers);
+ }
+
+ ///
+ /// Adds webhook subscription to if not exists
+ ///
+ public static void AddWebhookHeader(this WebhookSubscription webhookSubscription, string key, string value)
+ {
+ if (key.IsNullOrWhiteSpace() )
+ {
+ throw new ArgumentNullException(nameof(key), $"{nameof(key)} can not be null, empty or whitespace!");
+ }
+
+ if (value.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(value), $"{nameof(value)} can not be null, empty or whitespace!");
+ }
+
+ var headers = webhookSubscription.GetWebhookHeaders();
+ headers[key] = value;
+
+ webhookSubscription.SetHeaders(JsonConvert.SerializeObject(headers));
+ }
+
+ ///
+ /// Adds webhook subscription to if not exists
+ ///
+ ///
+ /// Key of header
+ public static void RemoveWebhookHeader(this WebhookSubscription webhookSubscription, string header)
+ {
+ if (header.IsNullOrWhiteSpace())
+ {
+ throw new ArgumentNullException(nameof(header), $"{nameof(header)} can not be null, empty or whitespace!");
+ }
+
+ var headers = webhookSubscription.GetWebhookHeaders();
+
+ if (!headers.ContainsKey(header))
+ {
+ return;
+ }
+
+ headers.Remove(header);
+
+ webhookSubscription.SetHeaders(JsonConvert.SerializeObject(headers));
+ }
+
+ ///
+ /// Clears all
+ ///
+ ///
+ public static void RemoveAllWebhookHeaders(this WebhookSubscription webhookSubscription)
+ {
+ webhookSubscription.SetHeaders(null);
+ }
+
+ public static WebhookSubscriptionInfo ToWebhookSubscriptionInfo(this WebhookSubscription webhookSubscription)
+ {
+ return new WebhookSubscriptionInfo
+ {
+ Id = webhookSubscription.Id,
+ TenantId = webhookSubscription.TenantId,
+ IsActive = webhookSubscription.IsActive,
+ Secret = webhookSubscription.Secret,
+ WebhookUri = webhookSubscription.WebhookUri,
+ Webhooks = webhookSubscription.GetSubscribedWebhooks(),
+ Headers = webhookSubscription.GetWebhookHeaders()
+ };
+ }
+ }
+}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs
index 4cf97509f..a94eb35ee 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs
@@ -17,5 +17,6 @@ public interface IWebhookSendRecordRepository : IRepository, IMultiTenant, IHasCreationTime,
string data,
Guid? tenantId = null) : base(id)
{
- WebhookName = webhookName;
- Data = data;
+ WebhookName = Check.NotNullOrWhiteSpace(webhookName, nameof(webhookName), WebhookEventRecordConsts.MaxWebhookNameLength);
+ Data = Check.Length(data, nameof(data), WebhookEventRecordConsts.MaxDataLength);
TenantId = tenantId;
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs
index 62d864e8b..ff14e571a 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs
@@ -11,20 +11,17 @@ public class WebhookEventStore : DomainService, IWebhookEventStore
{
protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetRequiredService>();
- protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected IWebhookEventRecordRepository WebhookEventRepository { get; }
public WebhookEventStore(
- IUnitOfWorkManager unitOfWorkManager,
IWebhookEventRecordRepository webhookEventRepository)
{
- UnitOfWorkManager = unitOfWorkManager;
WebhookEventRepository = webhookEventRepository;
}
+ [UnitOfWork]
public async virtual Task GetAsync(Guid? tenantId, Guid id)
{
- using var uow = UnitOfWorkManager.Begin();
using (CurrentTenant.Change(tenantId))
{
var record = await WebhookEventRepository.GetAsync(id);
@@ -33,9 +30,9 @@ public class WebhookEventStore : DomainService, IWebhookEventStore
}
}
+ [UnitOfWork]
public async virtual Task InsertAndGetIdAsync(WebhookEvent webhookEvent)
{
- using var uow = UnitOfWorkManager.Begin();
using (CurrentTenant.Change(webhookEvent.TenantId))
{
var record = new WebhookEventRecord(
@@ -49,8 +46,6 @@ public class WebhookEventStore : DomainService, IWebhookEventStore
await WebhookEventRepository.InsertAsync(record);
- await uow.SaveChangesAsync();
-
return record.Id;
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs
index 8e024decf..1b469c105 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs
@@ -7,7 +7,6 @@ using System.Net;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
-using Volo.Abp.Linq;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Uow;
@@ -16,150 +15,108 @@ namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookSendAttemptStore : DomainService, IWebhookSendAttemptStore
{
protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetRequiredService>();
- protected IAsyncQueryableExecuter AsyncQueryableExecuter => LazyServiceProvider.LazyGetRequiredService();
- protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected IWebhookSendRecordRepository WebhookSendAttemptRepository { get; }
public WebhookSendAttemptStore(
- IUnitOfWorkManager unitOfWorkManager,
IWebhookSendRecordRepository webhookSendAttemptRepository)
{
- UnitOfWorkManager = unitOfWorkManager;
WebhookSendAttemptRepository = webhookSendAttemptRepository;
}
+ [UnitOfWork]
public async virtual Task<(int TotalCount, IReadOnlyCollection Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(
Guid? tenantId,
Guid subscriptionId,
int maxResultCount,
int skipCount)
{
- (int TotalCount, IReadOnlyCollection Webhooks) sendAttempts;
-
- using (var uow = UnitOfWorkManager.Begin())
+ using (CurrentTenant.Change(tenantId))
{
- using (CurrentTenant.Change(tenantId))
+ var filter = new WebhookSendRecordFilter
{
- var filter = new WebhookSendRecordFilter
- {
- SubscriptionId = subscriptionId,
- };
- var totalCount = await WebhookSendAttemptRepository.GetCountAsync(filter);
-
- var list = await WebhookSendAttemptRepository.GetListAsync(
- filter,
- maxResultCount: maxResultCount,
- skipCount: skipCount);
+ SubscriptionId = subscriptionId,
+ };
+ var totalCount = await WebhookSendAttemptRepository.GetCountAsync(filter);
- var webHooks = ObjectMapper.Map, List>(list);
+ var list = await WebhookSendAttemptRepository.GetListAsync(
+ filter,
+ maxResultCount: maxResultCount,
+ skipCount: skipCount);
- sendAttempts = ValueTuple.Create(totalCount, webHooks.ToImmutableList());
- }
+ var webHooks = ObjectMapper.Map, List>(list);
- await uow.CompleteAsync();
+ return ValueTuple.Create(totalCount, webHooks.ToImmutableList());
}
-
- return sendAttempts;
}
+ [UnitOfWork]
public async virtual Task> GetAllSendAttemptsByWebhookEventIdAsync(
Guid? tenantId,
Guid webhookEventId)
{
- List sendAttempts;
-
- using (var uow = UnitOfWorkManager.Begin())
+ using (CurrentTenant.Change(tenantId))
{
- using (CurrentTenant.Change(tenantId))
- {
- var queryable = await WebhookSendAttemptRepository.GetQueryableAsync();
+ var queryable = await WebhookSendAttemptRepository.GetQueryableAsync();
- var list = await AsyncQueryableExecuter.ToListAsync(queryable
- .Where(attempt => attempt.WebhookEventId == webhookEventId)
- .OrderByDescending(attempt => attempt.CreationTime)
- );
-
- sendAttempts = ObjectMapper.Map, List>(list);
- }
+ var list = await AsyncExecuter.ToListAsync(queryable
+ .Where(attempt => attempt.WebhookEventId == webhookEventId)
+ .OrderByDescending(attempt => attempt.CreationTime)
+ );
- await uow.CompleteAsync();
+ return ObjectMapper.Map, List>(list);
}
-
- return sendAttempts;
}
+ [UnitOfWork]
public async virtual Task GetAsync(
Guid? tenantId,
Guid id)
{
- WebhookSendRecord sendAttempt;
-
- using (var uow = UnitOfWorkManager.Begin())
+ using (CurrentTenant.Change(tenantId))
{
- using (CurrentTenant.Change(tenantId))
- {
- sendAttempt = await WebhookSendAttemptRepository.GetAsync(id);
- }
+ var sendAttempt = await WebhookSendAttemptRepository.GetAsync(id);
- await uow.CompleteAsync();
+ return ObjectMapper.Map(sendAttempt);
}
-
- return ObjectMapper.Map(sendAttempt);
}
+ [UnitOfWork]
public async virtual Task GetSendAttemptCountAsync(
Guid? tenantId,
Guid webhookEventId,
Guid webhookSubscriptionId)
{
- int sendAttemptCount;
-
- using (var uow = UnitOfWorkManager.Begin())
+ using (CurrentTenant.Change(tenantId))
{
- using (CurrentTenant.Change(tenantId))
- {
- sendAttemptCount = await WebhookSendAttemptRepository.CountAsync(attempt =>
- attempt.WebhookEventId == webhookEventId &&
- attempt.WebhookSubscriptionId == webhookSubscriptionId);
- }
-
- await uow.CompleteAsync();
+ return await WebhookSendAttemptRepository.CountAsync(attempt =>
+ attempt.WebhookEventId == webhookEventId &&
+ attempt.WebhookSubscriptionId == webhookSubscriptionId);
}
-
- return sendAttemptCount;
}
+ [UnitOfWork]
public async virtual Task HasXConsecutiveFailAsync(
Guid? tenantId,
Guid subscriptionId,
int failCount)
{
- bool result;
-
- using (var uow = UnitOfWorkManager.Begin())
+ using (CurrentTenant.Change(tenantId))
{
- using (CurrentTenant.Change(tenantId))
+ if (await WebhookSendAttemptRepository.CountAsync(x => x.WebhookSubscriptionId == subscriptionId) < failCount)
{
- if (await WebhookSendAttemptRepository.CountAsync(x => x.WebhookSubscriptionId == subscriptionId) < failCount)
- {
- result = false;
- }
- else
- {
- var queryable = await WebhookSendAttemptRepository.GetQueryableAsync();
-
- result = !await AsyncQueryableExecuter.AnyAsync(queryable
- .OrderByDescending(attempt => attempt.CreationTime)
- .Take(failCount)
- .Where(attempt => attempt.ResponseStatusCode == HttpStatusCode.OK)
- );
- }
+ return false;
}
+ else
+ {
+ var queryable = await WebhookSendAttemptRepository.GetQueryableAsync();
- await uow.CompleteAsync();
+ return !await AsyncExecuter.AnyAsync(queryable
+ .OrderByDescending(attempt => attempt.CreationTime)
+ .Take(failCount)
+ .Where(attempt => attempt.ResponseStatusCode == HttpStatusCode.OK)
+ );
+ }
}
-
- return result;
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs
index 91c3c8a97..0d0d0115a 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs
@@ -1,5 +1,6 @@
using System;
using System.Net;
+using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
@@ -44,7 +45,7 @@ public class WebhookSendRecord : Entity, IHasCreationTime, IHasModificatio
string response,
HttpStatusCode? statusCode = null)
{
- Response = response;
+ Response = Check.Length(response, nameof(response), WebhookSendRecordConsts.MaxResponseLength);
ResponseStatusCode = statusCode;
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs
index 075a6152b..44f034c30 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs
@@ -11,8 +11,6 @@ public class WebhookSendRecordFilter
public Guid? SubscriptionId { get; set; }
- public string Response { get; set; }
-
public HttpStatusCode? ResponseStatusCode { get; set; }
public DateTime? BeginCreationTime { get; set; }
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs
index be342aaa3..4d637e88a 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs
@@ -1,4 +1,5 @@
using System;
+using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
namespace LINGYUN.Abp.WebhooksManagement;
@@ -22,12 +23,27 @@ public class WebhookSubscription : CreationAuditedEntity
string headers,
Guid? tenantId = null) : base(id)
{
- WebhookUri = webhookUri;
- Secret = secret;
- Webhooks = webhooks;
- Headers = headers;
+ Secret = Check.NotNullOrWhiteSpace(secret, nameof(secret), WebhookSubscriptionConsts.MaxSecretLength);
+ SetWebhookUri(webhookUri);
+ SetWebhooks(webhooks);
+ SetHeaders(headers);
TenantId = tenantId;
IsActive = true;
}
+
+ public void SetWebhookUri(string webhookUri)
+ {
+ WebhookUri = Check.NotNullOrWhiteSpace(webhookUri, nameof(webhookUri), WebhookSubscriptionConsts.MaxWebhookUriLength);
+ }
+
+ public void SetWebhooks(string webhooks)
+ {
+ Webhooks = Check.Length(webhooks, nameof(webhooks), WebhookSubscriptionConsts.MaxWebhooksLength);
+ }
+
+ public void SetHeaders(string headers)
+ {
+ Headers = Check.Length(headers, nameof(headers), WebhookSubscriptionConsts.MaxHeadersLength);
+ }
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs
index abdbf33be..c25467ef6 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs
@@ -1,55 +1,148 @@
using LINGYUN.Abp.Webhooks;
+using LINGYUN.Abp.WebhooksManagement.Extensions;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
+using Volo.Abp.Uow;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookSubscriptionsStore : DomainService, IWebhookSubscriptionsStore
{
- public Task DeleteAsync(Guid id)
+ protected IWebhookSubscriptionRepository SubscriptionRepository { get; }
+
+ public WebhookSubscriptionsStore(
+ IWebhookSubscriptionRepository subscriptionRepository)
+ {
+ SubscriptionRepository = subscriptionRepository;
+ }
+
+ [UnitOfWork]
+ public async virtual Task DeleteAsync(Guid id)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ await SubscriptionRepository.DeleteAsync(id);
+ }
}
- public Task> GetAllSubscriptionsAsync(Guid? tenantId)
+ [UnitOfWork]
+ public async virtual Task> GetAllSubscriptionsAsync(Guid? tenantId)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ var queryable = await SubscriptionRepository.GetQueryableAsync();
+
+ var subscriptions = await AsyncExecuter.ToListAsync(queryable.Where(x => x.TenantId == tenantId));
+
+ return subscriptions.Select(subscription => subscription.ToWebhookSubscriptionInfo()).ToList();
+ }
}
- public Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName)
+ [UnitOfWork]
+ public async virtual Task> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ var queryable = await SubscriptionRepository.GetQueryableAsync();
+
+ var subscriptions = await AsyncExecuter.ToListAsync(
+ queryable.Where(x =>
+ x.TenantId == tenantId &&
+ x.IsActive &&
+ x.Webhooks.Contains("\"" + webhookName + "\"")));
+
+ return subscriptions.Select(subscription => subscription.ToWebhookSubscriptionInfo()).ToList();
+ }
}
- public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
+ [UnitOfWork]
+ public async virtual Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ var queryable = await SubscriptionRepository.GetQueryableAsync();
+
+ var subscriptions = await AsyncExecuter.ToListAsync(queryable.Where(x => tenantIds.Contains(x.TenantId)));
+
+ return subscriptions.Select(subscription => subscription.ToWebhookSubscriptionInfo()).ToList();
+ }
}
- public Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName)
+ [UnitOfWork]
+ public async virtual Task> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ var queryable = await SubscriptionRepository.GetQueryableAsync();
+
+ var subscriptions = await AsyncExecuter.ToListAsync(
+ queryable.Where(x =>
+ x.IsActive &&
+ tenantIds.Contains(x.TenantId) &&
+ x.Webhooks.Contains("\"" + webhookName + "\"")));
+
+ return subscriptions.Select(subscription => subscription.ToWebhookSubscriptionInfo()).ToList();
+ }
}
- public Task GetAsync(Guid id)
+ [UnitOfWork]
+ public async virtual Task GetAsync(Guid id)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ var subscription = await SubscriptionRepository.GetAsync(id);
+
+ return subscription.ToWebhookSubscriptionInfo();
+ }
}
- public Task InsertAsync(WebhookSubscriptionInfo webhookSubscription)
+ [UnitOfWork]
+ public async virtual Task InsertAsync(WebhookSubscriptionInfo webhookSubscription)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ var subscription = new WebhookSubscription(
+ webhookSubscription.Id,
+ webhookSubscription.WebhookUri,
+ webhookSubscription.Secret,
+ JsonConvert.SerializeObject(webhookSubscription.Webhooks),
+ JsonConvert.SerializeObject(webhookSubscription.Headers),
+ webhookSubscription.TenantId);
+
+ await SubscriptionRepository.InsertAsync(subscription);
+ }
}
- public Task IsSubscribedAsync(Guid? tenantId, string webhookName)
+ [UnitOfWork]
+ public async virtual Task IsSubscribedAsync(Guid? tenantId, string webhookName)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(null))
+ {
+ var queryable = await SubscriptionRepository.GetQueryableAsync();
+
+ return await AsyncExecuter.AnyAsync(
+ queryable.Where(x =>
+ x.TenantId == tenantId &&
+ x.IsActive &&
+ x.Webhooks.Contains("\"" + webhookName + "\"")));
+ }
}
- public Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription)
+ [UnitOfWork]
+ public async virtual Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription)
{
- throw new NotImplementedException();
+ using (CurrentTenant.Change(webhookSubscription.TenantId))
+ {
+ var subscription = await SubscriptionRepository.GetAsync(webhookSubscription.Id);
+ subscription.SetWebhookUri(webhookSubscription.WebhookUri);
+ subscription.SetWebhooks(webhookSubscription.ToSubscribedWebhooksString());
+ subscription.SetHeaders(webhookSubscription.ToWebhookHeadersString());
+
+ await SubscriptionRepository.UpdateAsync(subscription);
+ }
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs
index cbef9c444..231739e73 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs
@@ -1,4 +1,8 @@
using AutoMapper;
+using LINGYUN.Abp.Webhooks;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
namespace LINGYUN.Abp.WebhooksManagement;
@@ -6,6 +10,11 @@ public class WebhooksManagementDomainMapperProfile : Profile
{
public WebhooksManagementDomainMapperProfile()
{
+ CreateMap();
+ CreateMap();
+ CreateMap();
+ CreateMap();
+ CreateMap();
}
}
diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs
index bdeb77779..18903b4b3 100644
--- a/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs
+++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs
@@ -1,8 +1,10 @@
using LINGYUN.Abp.Webhooks;
+using LINGYUN.Abp.WebhooksManagement.ObjectExtending;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.Modularity;
+using Volo.Abp.ObjectExtending.Modularity;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.WebhooksManagement;
@@ -25,6 +27,13 @@ public class WebhooksManagementDomainModule : AbpModule
Configure(options =>
{
+ options.EtoMappings.Add();
+ options.EtoMappings.Add