Browse Source

feat(webhooks): added support webhooks.

pull/532/head
cKey 4 years ago
parent
commit
fef1eb61eb
  1. 10
      aspnet-core/LINGYUN.MicroService.All.sln
  2. 10
      aspnet-core/LINGYUN.MicroService.Common.sln
  3. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml
  4. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd
  5. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj
  6. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs
  7. 26
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs
  8. 125
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs
  9. 125
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
  10. 133
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs
  11. 22
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs
  12. 31
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs
  13. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs
  14. 22
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs
  15. 56
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs
  16. 25
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs
  17. 16
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs
  18. 74
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs
  19. 83
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs
  20. 24
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs
  21. 47
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
  22. 65
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs
  23. 57
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs
  24. 54
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs
  25. 109
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs
  26. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs
  27. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs
  28. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs
  29. 84
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
  30. 35
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs
  31. 39
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs
  32. 67
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs
  33. 60
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs
  34. 175
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
  35. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs
  36. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml
  37. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd
  38. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj
  39. 22
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs
  40. 8
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs
  41. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs
  42. 6
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs
  43. 15
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs
  44. 7
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs
  45. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml
  46. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd
  47. 20
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj
  48. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs
  49. 10
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs
  50. 24
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs
  51. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml
  52. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd
  53. 16
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj
  54. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDaprClientModule.cs
  55. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml
  56. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd
  57. 25
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj
  58. 8
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/en.json
  59. 8
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/Resources/zh-Hans.json
  60. 8
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/Localization/WebhooksManagementResource.cs
  61. 16
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfiguration.cs
  62. 19
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConfigurationDictionaryExtensions.cs
  63. 11
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/ObjectExtending/WebhooksManagementModuleExtensionConsts.cs
  64. 32
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainSharedModule.cs
  65. 6
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN/Abp/WebhooksManagement/WebhooksManagementErrorCodes.cs
  66. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml
  67. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd
  68. 22
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj
  69. 70
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/DefaultWebhookManager.cs
  70. 8
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookEventRecordRepository.cs
  71. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordRepository.cs
  72. 8
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionRepository.cs
  73. 11
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettingDefinitionProvider.cs
  74. 7
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/Settings/WebhooksManagementSettings.cs
  75. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventRecord.cs
  76. 57
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookEventStore.cs
  77. 165
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendAttemptStore.cs
  78. 50
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecord.cs
  79. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordFilter.cs
  80. 33
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscription.cs
  81. 55
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionsStore.cs
  82. 11
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDbProperties.cs
  83. 11
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainMapperProfile.cs
  84. 43
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN/Abp/WebhooksManagement/WebhooksManagementDomainModule.cs
  85. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml
  86. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd
  87. 19
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj
  88. 9
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/IWebhooksManagementDbContext.cs
  89. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContext.cs
  90. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementDbContextModelCreatingExtensions.cs
  91. 6
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEfCoreQueryableExtensions.cs
  92. 19
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementEntityFrameworkCoreModule.cs
  93. 17
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN/Abp/WebhooksManagement/EntityFrameworkCore/WebhooksManagementModelBuilderConfigurationOptions.cs
  94. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml
  95. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd
  96. 19
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj
  97. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN/Abp/WebhooksManagement/WebhooksManagementHttpApiClientModule.cs
  98. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml
  99. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd
  100. 19
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj

10
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}

10
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}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd

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

18
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Features" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Http.Client" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
</Project>

18
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)
{
}
}

26
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<WebhookDefinitionProvider> DefinitionProviders { get; }
public AbpWebhooksOptions()
{
TimeoutDuration = TimeSpan.FromSeconds(60);
MaxSendAttemptCount = 5;
MaxConsecutiveFailCountBeforeDeactivateSubscription = MaxSendAttemptCount * 3;
DefinitionProviders = new TypeList<WebhookDefinitionProvider>();
}
}

125
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<WebhookSenderArgs>, 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<AbpWebhooksOptions> 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<bool> 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;
}
}
}
}

125
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<WebhookSubscriptionInfo> 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<WebhookEvent> 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;
}
}
}

133
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<DefaultWebhookSender> Logger { protected get; set; }
private readonly AbpWebhooksOptions _options;
private readonly IWebhookManager _webhookManager;
private const string FailedRequestDefaultContent = "Webhook Send Request Failed";
public DefaultWebhookSender(
IOptions<AbpWebhooksOptions> options,
IWebhookManager webhookManager)
{
_options = options.Value;
_webhookManager = webhookManager;
Logger = NullLogger<DefaultWebhookSender>.Instance;
}
public async Task<Guid> 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;
}
/// <summary>
/// You can override this to change request message
/// </summary>
/// <returns></returns>
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);
}
}
}
}

22
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs

@ -0,0 +1,22 @@
namespace LINGYUN.Abp.Webhooks
{
public interface IWebhookDefinitionContext
{
/// <summary>
/// Adds the specified webhook definition. Throws exception if it is already added
/// </summary>
void Add(params WebhookDefinition[] definitions);
/// <summary>
/// Gets a webhook definition by name.
/// Returns null if there is no webhook definition with given name.
/// </summary>
WebhookDefinition GetOrNull(string name);
/// <summary>
/// Remove webhook with given name
/// </summary>
/// <param name="name">webhook definition name</param>
void Remove(string name);
}
}

31
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
{
/// <summary>
/// Gets a webhook definition by name.
/// Returns null if there is no webhook definition with given name.
/// </summary>
WebhookDefinition GetOrNull(string name);
/// <summary>
/// Gets a webhook definition by name.
/// Throws exception if there is no webhook definition with given name.
/// </summary>
WebhookDefinition Get(string name);
/// <summary>
/// Gets all webhook definitions.
/// </summary>
IReadOnlyList<WebhookDefinition> GetAll();
/// <summary>
/// Checks if given webhook name is available for given tenant.
/// </summary>
Task<bool> IsAvailableAsync(Guid? tenantId, string name);
}
}

18
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
{
/// <summary>
/// Inserts to persistent store
/// </summary>
Task<Guid> InsertAndGetIdAsync(WebhookEvent webhookEvent);
/// <summary>
/// Gets Webhook info by id
/// </summary>
Task<WebhookEvent> GetAsync(Guid? tenantId, Guid id);
}
}

22
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<WebhookPayload> GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs);
void SignWebhookRequest(HttpRequestMessage request, string serializedBody, string secret);
Task<string> GetSerializedBodyAsync(WebhookSenderArgs webhookSenderArgs);
Task<Guid> InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs);
Task StoreResponseOnWebhookSendAttemptAsync(
Guid webhookSendAttemptId, Guid? tenantId,
HttpStatusCode? statusCode, string content);
}
}

56
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
{
/// <summary>
/// Sends webhooks to current tenant subscriptions (<see cref="IAbpSession.TenantId"/>). with given data, (Checks permissions)
/// </summary>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
/// <param name="data">data to send</param>
/// <param name="sendExactSameData">
/// True: It sends the exact same data as the parameter to clients.
/// <para>
/// False: It sends data in <see cref="WebhookPayload"/>. It is recommended way.
/// </para>
/// </param>
/// <param name="headers">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.</param>
Task PublishAsync(string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null);
/// <summary>
/// Sends webhooks to given tenant's subscriptions
/// </summary>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
/// <param name="data">data to send</param>
/// <param name="tenantId">
/// Target tenant id
/// </param>
/// <param name="sendExactSameData">
/// True: It sends the exact same data as the parameter to clients.
/// <para>
/// False: It sends data in <see cref="WebhookPayload"/>. It is recommended way.
/// </para>
/// </param>
/// <param name="headers">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.</param>
Task PublishAsync(string webhookName, object data, Guid? tenantId, bool sendExactSameData = false, WebhookHeader headers = null);
/// <summary>
/// Sends webhooks to given tenant's subscriptions
/// </summary>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
/// <param name="data">data to send</param>
/// <param name="tenantIds">
/// Target tenant id(s)
/// </param>
/// <param name="sendExactSameData">
/// True: It sends the exact same data as the parameter to clients.
/// <para>
/// False: It sends data in <see cref="WebhookPayload"/>. It is recommended way.
/// </para>
/// </param>
/// <param name="headers">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.</param>
Task PublishAsync(Guid?[] tenantIds, string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null);
}
}

25
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<WebhookSendAttempt> GetAsync(Guid? tenantId, Guid id);
/// <summary>
/// Returns work item count by given web hook id and subscription id, (How many times publisher tried to send web hook)
/// </summary>
Task<int> GetSendAttemptCountAsync(Guid? tenantId, Guid webhookId, Guid webhookSubscriptionId);
/// <summary>
/// Checks is there any successful webhook attempt in last <paramref name="searchCount"/> items. Should return true if there are not X number items
/// </summary>
Task<bool> HasXConsecutiveFailAsync(Guid? tenantId, Guid subscriptionId, int searchCount);
Task<(int TotalCount, IReadOnlyCollection<WebhookSendAttempt> Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount, int skipCount);
Task<List<WebhookSendAttempt>> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId);
}
}

16
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
{
/// <summary>
/// Tries to send webhook with given transactionId and stores process in <see cref="WebhookSendAttempt"/>
/// Should throw exception if fails or response status not succeed
/// </summary>
/// <param name="webhookSenderArgs">arguments</param>
/// <returns>Webhook send attempt id</returns>
Task<Guid> SendWebhookAsync(WebhookSenderArgs webhookSenderArgs);
}
}

74
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
{
/// <summary>
/// Returns subscription for given id.
/// </summary>
/// <param name="id">Unique identifier of <see cref="WebhookSubscriptionInfo"/></param>
Task<WebhookSubscriptionInfo> GetAsync(Guid id);
/// <summary>
/// Returns all subscriptions of tenant
/// </summary>
/// <param name="tenantId">
/// Target tenant id.
/// </param>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId);
/// <summary>
/// Returns all subscriptions for given webhook.
/// </summary>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
/// <param name="tenantId">
/// Target tenant id.
/// </param>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsIfFeaturesGrantedAsync(Guid? tenantId, string webhookName);
/// <summary>
/// Returns all subscriptions of tenant
/// </summary>
/// <returns></returns>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds);
/// <summary>
/// Returns all subscriptions for given webhook.
/// </summary>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
/// <param name="tenantIds">
/// Target tenant id(s).
/// </param>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(Guid?[] tenantIds, string webhookName);
/// <summary>
/// Checks if tenant subscribed for a webhook. (Checks if webhook features are granted)
/// </summary>
/// <param name="tenantId">
/// Target tenant id(s).
/// </param>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
Task<bool> IsSubscribedAsync(Guid? tenantId, string webhookName);
/// <summary>
/// If id is the default(Guid) adds new subscription, else updates current one. (Checks if webhook features are granted)
/// </summary>
Task AddOrUpdateSubscriptionAsync(WebhookSubscriptionInfo webhookSubscription);
/// <summary>
/// Activates/Deactivates given webhook subscription
/// </summary>
/// <param name="id">unique identifier of <see cref="WebhookSubscriptionInfo"/></param>
/// <param name="active">IsActive</param>
Task ActivateWebhookSubscriptionAsync(Guid id, bool active);
/// <summary>
/// Delete given webhook subscription.
/// </summary>
/// <param name="id">unique identifier of <see cref="WebhookSubscriptionInfo"/></param>
Task DeleteSubscriptionAsync(Guid id);
}
}

83
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
{
/// <summary>
/// This interface should be implemented by vendors to make webhooks working.
/// </summary>
public interface IWebhookSubscriptionsStore
{
/// <summary>
/// returns subscription
/// </summary>
/// <param name="id">webhook subscription id</param>
/// <returns></returns>
Task<WebhookSubscriptionInfo> GetAsync(Guid id);
/// <summary>
/// Saves webhook subscription to a persistent store.
/// </summary>
/// <param name="webhookSubscription">webhook subscription information</param>
Task InsertAsync(WebhookSubscriptionInfo webhookSubscription);
/// <summary>
/// Updates webhook subscription to a persistent store.
/// </summary>
/// <param name="webhookSubscription">webhook subscription information</param>
Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription);
/// <summary>
/// Deletes subscription if exists
/// </summary>
/// <param name="id"><see cref="WebhookSubscriptionInfo"/> primary key</param>
/// <returns></returns>
Task DeleteAsync(Guid id);
/// <summary>
/// Returns all subscriptions of given tenant including deactivated
/// </summary>
/// <param name="tenantId">
/// Target tenant id.
/// </param>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId);
/// <summary>
/// Returns webhook subscriptions which subscribe to given webhook on tenant(s)
/// </summary>
/// <param name="tenantId">
/// Target tenant id.
/// </param>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
/// <returns></returns>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName);
/// <summary>
/// Returns all subscriptions of given tenant including deactivated
/// </summary>
/// <param name="tenantIds">
/// Target tenant id(s).
/// </param>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds);
/// <summary>
/// Returns webhook subscriptions which subscribe to given webhook on tenant(s)
/// </summary>
/// <param name="tenantIds">
/// Target tenant id(s).
/// </param>
/// <param name="webhookName"><see cref="WebhookDefinition.Name"/></param>
/// <returns></returns>
Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName);
/// <summary>
/// Checks if tenant subscribed for a webhook
/// </summary>
/// <param name="tenantId">
/// Target tenant id(s).
/// </param>
/// <param name="webhookName">Name of the webhook</param>
Task<bool> IsSubscribedAsync(Guid? tenantId, string webhookName);
}
}

24
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
{
/// <summary>
/// Null pattern implementation of <see cref="IWebhookSubscriptionsStore"/>.
/// It's used if <see cref="IWebhookSubscriptionsStore"/> is not implemented by actual persistent store
/// </summary>
public class NullWebhookEventStore : IWebhookEventStore
{
public static NullWebhookEventStore Instance { get; } = new NullWebhookEventStore();
public Task<Guid> InsertAndGetIdAsync(WebhookEvent webhookEvent)
{
return Task.FromResult<Guid>(default);
}
public Task<WebhookEvent> GetAsync(Guid? tenantId, Guid id)
{
return Task.FromResult<WebhookEvent>(default);
}
}
}

47
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<WebhookSendAttempt> GetAsync(Guid? tenantId, Guid id)
{
return Task.FromResult<WebhookSendAttempt>(default);
}
public Task<int> GetSendAttemptCountAsync(Guid? tenantId, Guid webhookId, Guid webhookSubscriptionId)
{
return Task.FromResult(int.MaxValue);
}
public Task<bool> HasXConsecutiveFailAsync(Guid? tenantId, Guid subscriptionId, int searchCount)
{
return default;
}
public Task<IReadOnlyCollection<WebhookSendAttempt>> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount,
int skipCount)
{
return Task.FromResult(new List<WebhookSendAttempt>() as IReadOnlyCollection<WebhookSendAttempt>);
}
public Task<List<WebhookSendAttempt>> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId)
{
return Task.FromResult(new List<WebhookSendAttempt>());
}
}
}

65
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
{
/// <summary>
/// Null pattern implementation of <see cref="IWebhookSubscriptionsStore"/>.
/// It's used if <see cref="IWebhookSubscriptionsStore"/> is not implemented by actual persistent store
/// </summary>
public class NullWebhookSubscriptionsStore : IWebhookSubscriptionsStore
{
public static NullWebhookSubscriptionsStore Instance { get; } = new NullWebhookSubscriptionsStore();
public Task<WebhookSubscriptionInfo> GetAsync(Guid id)
{
return Task.FromResult<WebhookSubscriptionInfo>(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<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId)
{
return Task.FromResult(new List<WebhookSubscriptionInfo>());
}
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName)
{
return Task.FromResult(new List<WebhookSubscriptionInfo>());
}
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
{
return Task.FromResult(new List<WebhookSubscriptionInfo>());
}
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName)
{
return Task.FromResult(new List<WebhookSubscriptionInfo>());
}
public Task<bool> IsSubscribedAsync(Guid? tenantId, string webhookName)
{
return Task.FromResult(false);
}
}
}

57
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
{
/// <summary>
/// Unique name of the webhook.
/// </summary>
public string Name { get; }
/// <summary>
/// Display name of the webhook.
/// Optional.
/// </summary>
public ILocalizableString DisplayName { get; set; }
/// <summary>
/// Description for the webhook.
/// Optional.
/// </summary>
public ILocalizableString Description { get; set; }
public List<string> 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<string>();
}
public WebhookDefinition WithFeature(params string[] features)
{
if (!features.IsNullOrEmpty())
{
RequiredFeatures.AddRange(features);
}
return this;
}
public override string ToString()
{
return $"[{nameof(WebhookDefinition)} {Name}]";
}
}
}

54
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<string, WebhookDefinition> Webhooks { get; }
public WebhookDefinitionContext(Dictionary<string, WebhookDefinition> 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);
}
}
}

109
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<string, WebhookDefinition> WebhookDefinitions => _lazyWebhookDefinitions.Value;
private readonly Lazy<Dictionary<string, WebhookDefinition>> _lazyWebhookDefinitions;
private readonly IServiceProvider _serviceProvider;
private readonly AbpWebhooksOptions _options;
public WebhookDefinitionManager(
IServiceProvider serviceProvider,
IOptions<AbpWebhooksOptions> options)
{
_serviceProvider = serviceProvider;
_options = options.Value;
_lazyWebhookDefinitions = new Lazy<Dictionary<string, WebhookDefinition>>(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<WebhookDefinition> GetAll()
{
return WebhookDefinitions.Values.ToImmutableList();
}
public async Task<bool> 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<ICurrentTenant>();
var featureChecker = _serviceProvider.GetRequiredService<IFeatureChecker>();
using (currentTenant.Change(tenantId))
{
if (!await featureChecker.IsEnabledAsync(true, webhookDefinition.RequiredFeatures.ToArray()))
{
return false;
}
}
return true;
}
protected virtual Dictionary<string, WebhookDefinition> CreateWebhookDefinitions()
{
var definitions = new Dictionary<string, WebhookDefinition>();
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;
}
}
}

13
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
{
/// <summary>
/// Used to add/manipulate webhook definitions.
/// </summary>
/// <param name="context">Context</param>,
public abstract void Define(IWebhookDefinitionContext context);
}
}

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs

@ -0,0 +1,30 @@
using System;
namespace LINGYUN.Abp.Webhooks
{
/// <summary>
/// Store created web hooks. To see who get that webhook check with <see cref="WebhookSendAttempt.WebhookEventId"/> and you can get <see cref="WebhookSendAttempt.WebhookSubscriptionId"/>
/// </summary>
public class WebhookEvent
{
public Guid Id { get; set; }
/// <summary>
/// Webhook unique name <see cref="WebhookDefinition.Name"/>
/// </summary>
public string WebhookName { get; set; }
/// <summary>
/// Webhook data as JSON string.
/// </summary>
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; }
}
}

18
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
{
/// <summary>
/// If true, webhook will only contain given headers. If false given headers will be added to predefined headers in subscription.
/// Default is false
/// </summary>
public bool UseOnlyGivenHeaders { get; set; }
/// <summary>
/// That headers will be sent with the webhook.
/// </summary>
public IDictionary<string, string> Headers { get; set; }
}
}

84
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<WebhookPayload> 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<string> GetSerializedBodyAsync(WebhookSenderArgs webhookSenderArgs)
{
if (webhookSenderArgs.SendExactSameData)
{
return webhookSenderArgs.Data;
}
var payload = await GetWebhookPayloadAsync(webhookSenderArgs);
var serializedBody = JsonSerializer.Serialize(payload);
return serializedBody;
}
public abstract Task<Guid> InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs);
public abstract Task StoreResponseOnWebhookSendAttemptAsync(Guid webhookSendAttemptId, Guid? tenantId, HttpStatusCode? statusCode, string content);
}
}

35
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;
}
}
}

39
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
{
/// <summary>
/// Table for store webhook work items. Each item stores web hook send attempt of <see cref="WebhookEvent"/> to subscribed tenants
/// </summary>
public class WebhookSendAttempt
{
public Guid Id { get; set; }
/// <summary>
/// <see cref="WebhookEvent"/> foreign id
/// </summary>
public Guid WebhookEventId { get; set; }
/// <summary>
/// <see cref="WebhookSubscription"/> foreign id
/// </summary>
public Guid WebhookSubscriptionId { get; set; }
/// <summary>
/// Webhook response content that webhook endpoint send back
/// </summary>
public string Response { get; set; }
/// <summary>
/// Webhook response status code that webhook endpoint send back
/// </summary>
public HttpStatusCode? ResponseStatusCode { get; set; }
public DateTime CreationTime { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? TenantId { get; set; }
}
}

67
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
/// <summary>
/// <see cref="WebhookEvent"/> foreign id
/// </summary>
public Guid WebhookEventId { get; set; }
/// <summary>
/// Webhook unique name
/// </summary>
public string WebhookName { get; set; }
/// <summary>
/// Webhook data as JSON string.
/// </summary>
public string Data { get; set; }
//Subscription information
/// <summary>
/// <see cref="WebhookSubscription"/> foreign id
/// </summary>
public Guid WebhookSubscriptionId { get; set; }
/// <summary>
/// Subscription webhook endpoint
/// </summary>
public string WebhookUri { get; set; }
/// <summary>
/// Webhook secret
/// </summary>
public string Secret { get; set; }
/// <summary>
/// Gets a set of additional HTTP headers.That headers will be sent with the webhook.
/// </summary>
public IDictionary<string, string> Headers { get; set; }
/// <summary>
/// Tries to send webhook only one time without checking to send attempt count
/// </summary>
public bool TryOnce { get; set; }
/// <summary>
/// True: It sends the exact same data as the parameter to clients.
/// <para>
/// False: It sends data in <see cref="WebhookPayload"/>. It is recommended way.
/// </para>
/// </summary>
public bool SendExactSameData { get; set; }
public WebhookSenderArgs()
{
Headers = new Dictionary<string, string>();
}
}
}

60
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; }
/// <summary>
/// Subscribed Tenant's id .
/// </summary>
public Guid? TenantId { get; set; }
/// <summary>
/// Subscription webhook endpoint
/// </summary>
public string WebhookUri { get; set; }
/// <summary>
/// Webhook secret
/// </summary>
public string Secret { get; set; }
/// <summary>
/// Is subscription active
/// </summary>
public bool IsActive { get; set; }
/// <summary>
/// Subscribed webhook definitions unique names.It contains webhook definitions list as json
/// <para>
/// Do not change it manually.
/// Use <see cref=" WebhookSubscriptionInfoExtensions.GetSubscribedWebhooks"/>,
/// <see cref=" WebhookSubscriptionInfoExtensions.SubscribeWebhook"/>,
/// <see cref="WebhookSubscriptionInfoExtensions.UnsubscribeWebhook"/> and
/// <see cref="WebhookSubscriptionInfoExtensions.RemoveAllSubscribedWebhooks"/> to change it.
/// </para>
/// </summary>
public List<string> Webhooks { get; set; }
/// <summary>
/// Gets a set of additional HTTP headers.That headers will be sent with the webhook. It contains webhook header dictionary as json
/// <para>
/// Do not change it manually.
/// Use <see cref=" WebhookSubscriptionInfoExtensions.GetWebhookHeaders"/>,
/// <see cref="WebhookSubscriptionInfoExtensions.AddWebhookHeader"/>,
/// <see cref="WebhookSubscriptionInfoExtensions.RemoveWebhookHeader"/>,
/// <see cref="WebhookSubscriptionInfoExtensions.RemoveAllWebhookHeaders"/> to change it.
/// </para>
/// </summary>
public IDictionary<string, string> Headers { get; set; }
public WebhookSubscriptionInfo()
{
IsActive = true;
Headers = new Dictionary<string, string>();
Webhooks = new List<string>();
}
}
}

175
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<WebhookSubscriptionInfo> GetAsync(Guid id)
{
return await WebhookSubscriptionsStore.GetAsync(id);
}
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId)
{
return await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId);
}
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsIfFeaturesGrantedAsync(Guid? tenantId, string webhookName)
{
if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName))
{
return new List<WebhookSubscriptionInfo>();
}
return (await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId, webhookName)).ToList();
}
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
{
return (await WebhookSubscriptionsStore.GetAllSubscriptionsOfTenantsAsync(tenantIds)).ToList();
}
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(Guid?[] tenantIds, string webhookName)
{
var featureGrantedTenants = new List<Guid?>();
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<bool> 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
}
}

13
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));
}
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd

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

21
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Authorization" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Features" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Domain.Shared\LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj" />
</ItemGroup>
</Project>

22
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<WebhooksManagementResource>(name);
}
}

8
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";
}

18
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<WebhooksManagementResource>(name);
}
}

6
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";
}

15
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
{
}

7
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";
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd

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

20
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Application.Contracts\LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Domain\LINGYUN.Abp.WebhooksManagement.Domain.csproj" />
</ItemGroup>
</Project>

13
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);
}
}

10
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()
{
}
}

24
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<WebhooksManagementApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<WebhooksManagementApplicationMapperProfile>(validate: true);
});
}
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xsd

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

16
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/LINGYUN.Abp.WebhooksManagement.Dapr.Client.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\dapr\LINGYUN.Abp.Dapr.Client\LINGYUN.Abp.Dapr.Client.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Application.Contracts\LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj" />
</ItemGroup>
</Project>

18
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);
}
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/FodyWeavers.xsd

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

25
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain.Shared/LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\WebhooksManagement\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\WebhooksManagement\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Auditing" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Localization" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
</Project>

8
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"
}
}

8
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": "管理设置"
}
}

8
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
{
}

16
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<EntityExtensionConfiguration> configureAction)
{
return this.ConfigureEntity(
WebhooksManagementModuleExtensionConsts.EntityNames.Entity,
configureAction
);
}
}

19
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<WebhooksManagementModuleExtensionConfiguration> configureAction)
{
return modules.ConfigureModule(
WebhooksManagementModuleExtensionConsts.ModuleName,
configureAction
);
}
}

11
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";
}
}

32
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<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<WebhooksManagementDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<WebhooksManagementResource>()
.AddVirtualJson("/LINGYUN/Abp/WebhooksManagement/Localization/Resources");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace(WebhooksManagementErrorCodes.Namespace, typeof(WebhooksManagementResource));
});
}
}

6
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";
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/FodyWeavers.xsd

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

22
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Domain/LINGYUN.Abp.WebhooksManagement.Domain.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Caching" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Domain.Shared\LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.WebHooks\LINGYUN.Abp.WebHooks.csproj" />
</ItemGroup>
</Project>

70
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<Guid> 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();
}
}
}
}

8
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<WebhookEventRecord, Guid>
{
}

21
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<WebhookSendRecord, Guid>
{
Task<int> GetCountAsync(
WebhookSendRecordFilter filter,
CancellationToken cancellationToken = default);
Task<List<WebhookSendRecord>> GetListAsync(
WebhookSendRecordFilter filter,
string sorting = nameof(WebhookSendRecord.CreationTime),
int maxResultCount = 10,
int skipCount = 10,
CancellationToken cancellationToken = default);
}

8
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<WebhookSubscription, Guid>
{
}

11
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)
{
}
}
}

7
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";
}
}

30
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<Guid>, 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;
}
}

57
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<WebhooksManagementDomainModule> ObjectMapper => LazyServiceProvider.LazyGetRequiredService<IObjectMapper<WebhooksManagementDomainModule>>();
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected IWebhookEventRecordRepository WebhookEventRepository { get; }
public WebhookEventStore(
IUnitOfWorkManager unitOfWorkManager,
IWebhookEventRecordRepository webhookEventRepository)
{
UnitOfWorkManager = unitOfWorkManager;
WebhookEventRepository = webhookEventRepository;
}
public async virtual Task<WebhookEvent> GetAsync(Guid? tenantId, Guid id)
{
using var uow = UnitOfWorkManager.Begin();
using (CurrentTenant.Change(tenantId))
{
var record = await WebhookEventRepository.GetAsync(id);
return ObjectMapper.Map<WebhookEventRecord, WebhookEvent>(record);
}
}
public async virtual Task<Guid> 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;
}
}
}

165
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<WebhooksManagementDomainModule> ObjectMapper => LazyServiceProvider.LazyGetRequiredService<IObjectMapper<WebhooksManagementDomainModule>>();
protected IAsyncQueryableExecuter AsyncQueryableExecuter => LazyServiceProvider.LazyGetRequiredService<IAsyncQueryableExecuter>();
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<WebhookSendAttempt> Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(
Guid? tenantId,
Guid subscriptionId,
int maxResultCount,
int skipCount)
{
(int TotalCount, IReadOnlyCollection<WebhookSendAttempt> 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<WebhookSendRecord>, List<WebhookSendAttempt>>(list);
sendAttempts = ValueTuple.Create(totalCount, webHooks.ToImmutableList());
}
await uow.CompleteAsync();
}
return sendAttempts;
}
public async virtual Task<List<WebhookSendAttempt>> GetAllSendAttemptsByWebhookEventIdAsync(
Guid? tenantId,
Guid webhookEventId)
{
List<WebhookSendAttempt> 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<WebhookSendRecord>, List<WebhookSendAttempt>>(list);
}
await uow.CompleteAsync();
}
return sendAttempts;
}
public async virtual Task<WebhookSendAttempt> 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<WebhookSendRecord, WebhookSendAttempt>(sendAttempt);
}
public async virtual Task<int> 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<bool> 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;
}
}

50
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<Guid>, 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;
}
}

21
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; }
}

33
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<Guid>
{
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;
}
}

55
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<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId)
{
throw new NotImplementedException();
}
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName)
{
throw new NotImplementedException();
}
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds)
{
throw new NotImplementedException();
}
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName)
{
throw new NotImplementedException();
}
public Task<WebhookSubscriptionInfo> GetAsync(Guid id)
{
throw new NotImplementedException();
}
public Task InsertAsync(WebhookSubscriptionInfo webhookSubscription)
{
throw new NotImplementedException();
}
public Task<bool> IsSubscribedAsync(Guid? tenantId, string webhookName)
{
throw new NotImplementedException();
}
public Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription)
{
throw new NotImplementedException();
}
}

11
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";
}

11
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()
{
}
}

43
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<WebhooksManagementDomainModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<WebhooksManagementDomainMapperProfile>(validate: true);
});
Configure<AbpDistributedEntityEventOptions>(options =>
{
});
}
public override void PostConfigureServices(ServiceConfigurationContext context)
{
OneTimeRunner.Run(() =>
{
// 扩展实体配置
//ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity(
// WebhooksManagementModuleExtensionConsts.ModuleName,
// WebhooksManagementModuleExtensionConsts.EntityNames.Entity,
// typeof(Entity)
//);
});
}
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/FodyWeavers.xsd

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

19
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore/LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Domain\LINGYUN.Abp.WebhooksManagement.Domain.csproj" />
</ItemGroup>
</Project>

9
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
{
}

21
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<WebhooksManagementDbContext>, IWebhooksManagementDbContext
{
public WebhooksManagementDbContext(
DbContextOptions<WebhooksManagementDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureWebhooksManagement();
}
}

21
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<WebhooksManagementModelBuilderConfigurationOptions> optionsAction = null)
{
Check.NotNull(builder, nameof(builder));
var options = new WebhooksManagementModelBuilderConfigurationOptions(
WebhooksManagementDbProperties.DbTablePrefix,
WebhooksManagementDbProperties.DbSchema
);
optionsAction?.Invoke(options);
}
}

6
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
{
// 在此聚合仓储服务的扩展方法
}

19
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<WebhooksManagementDbContext>(options =>
{
options.AddDefaultRepositories<IWebhooksManagementDbContext>();
});
}
}

17
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)
{
}
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/FodyWeavers.xsd

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

19
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi.Client/LINGYUN.Abp.WebhooksManagement.HttpApi.Client.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Http.Client" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Application.Contracts\LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj" />
</ItemGroup>
</Project>

18
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);
}
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xml

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

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/FodyWeavers.xsd

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

19
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.HttpApi/LINGYUN.Abp.WebhooksManagement.HttpApi.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Application.Contracts\LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj" />
</ItemGroup>
</Project>

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

Loading…
Cancel
Save