Browse Source

Merge pull request #532 from colinin/support-webhooks

feat(webhooks): added support webhooks.
pull/533/head
yx lin 4 years ago
committed by GitHub
parent
commit
3a54359934
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      aspnet-core/Directory.Build.props
  2. 10
      aspnet-core/LINGYUN.MicroService.All.sln
  3. 10
      aspnet-core/LINGYUN.MicroService.Common.sln
  4. 16
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks.ClientProxies/LINGYUN.Abp.WebHooks.ClientProxies.csproj
  5. 10
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks.ClientProxies/LINGYUN/Abp/WebHooks/ClientProxies/AbpWebHooksClientProxiesModule.cs
  6. 91
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks.ClientProxies/LINGYUN/Abp/WebHooks/ClientProxies/ClientProxiesWebhookPublisher.cs
  7. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xml
  8. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/FodyWeavers.xsd
  9. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN.Abp.WebHooks.csproj
  10. 54
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs
  11. 35
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs
  12. 131
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs
  13. 140
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs
  14. 129
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs
  15. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/Extensions/WebhookSubscriptionExtensions.cs
  16. 16
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionContext.cs
  17. 37
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs
  18. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookEventStore.cs
  19. 22
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookManager.cs
  20. 56
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookPublisher.cs
  21. 25
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSendAttemptStore.cs
  22. 16
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSender.cs
  23. 74
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionManager.cs
  24. 83
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookSubscriptionsStore.cs
  25. 24
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookEventStore.cs
  26. 47
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSendAttemptStore.cs
  27. 65
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/NullWebhookSubscriptionsStore.cs
  28. 67
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs
  29. 55
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs
  30. 139
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs
  31. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionProvider.cs
  32. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookEvent.cs
  33. 101
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookGroupDefinition.cs
  34. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookHeader.cs
  35. 80
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs
  36. 35
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookPayload.cs
  37. 39
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSendAttempt.cs
  38. 62
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs
  39. 60
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionInfo.cs
  40. 172
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs
  41. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/System/AbpStringCryptographyExtensions.cs
  42. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/FodyWeavers.xml
  43. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/FodyWeavers.xsd
  44. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN.Abp.Webhooks.Identity.csproj
  45. 32
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/AbpWebhooksIdentityModule.cs
  46. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityRoleNameChangedWto.cs
  47. 63
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityRoleWebHooker.cs
  48. 11
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityRoleWto.cs
  49. 55
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityUserWebHooker.cs
  50. 23
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityUserWto.cs
  51. 64
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityWebhookDefinitionProvider.cs
  52. 22
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityWebhookNames.cs
  53. 20
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/Localization/Resources/en.json
  54. 20
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/Localization/Resources/zh-Hans.json
  55. 49
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/OrganizationUnitWebHooker.cs
  56. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/OrganizationUnitWto.cs
  57. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/FodyWeavers.xml
  58. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/FodyWeavers.xsd
  59. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN.Abp.Webhooks.Saas.csproj
  60. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/AbpWebhooksSaasModule.cs
  61. 48
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/EditionWebhooker.cs
  62. 11
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/EditionWto.cs
  63. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/Localization/Resources/en.json
  64. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/Localization/Resources/zh-Hans.json
  65. 60
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/SaasWebhookDefinitionProvider.cs
  66. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/SaasWebhookNames.cs
  67. 48
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/TenantWebhooker.cs
  68. 12
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/TenantWto.cs
  69. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xml
  70. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/FodyWeavers.xsd
  71. 21
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj
  72. 62
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs
  73. 27
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs
  74. 18
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureDefinitionProvider.cs
  75. 6
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Features/WebhooksManagementFeatureNames.cs
  76. 9
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/IWebhookPublishAppService.cs
  77. 15
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordAppService.cs
  78. 17
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionAppService.cs
  79. 8
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookAvailableDto.cs
  80. 10
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookAvailableGroupDto.cs
  81. 12
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookEventRecordDto.cs
  82. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookPublishInput.cs
  83. 24
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordDto.cs
  84. 20
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordGetListInput.cs
  85. 32
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionCreateOrUpdateInput.cs
  86. 15
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionDto.cs
  87. 26
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionGetListInput.cs
  88. 15
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationContractsModule.cs
  89. 7
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhooksManagementRemoteServiceConsts.cs
  90. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xml
  91. 30
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/FodyWeavers.xsd
  92. 20
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN.Abp.WebhooksManagement.Application.csproj
  93. 44
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionExtensions.cs
  94. 45
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhookPublishAppService.cs
  95. 74
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordAppService.cs
  96. 150
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionAppService.cs
  97. 13
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementAppServiceBase.cs
  98. 12
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs
  99. 24
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationModule.cs
  100. 3
      aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Dapr.Client/FodyWeavers.xml

5
aspnet-core/Directory.Build.props

@ -12,6 +12,11 @@
<HangfireMySqlStoragePackageVersion>2.0.3</HangfireMySqlStoragePackageVersion>
<HangfireMSSQLStoragePackageVersion>1.7.28</HangfireMSSQLStoragePackageVersion>
<NESTPackageVersion>7.15.1</NESTPackageVersion>
<OpenTelemetryExtensionsHostingPackageVersion>1.0.0-rc8</OpenTelemetryExtensionsHostingPackageVersion>
<OpenTelemetryExporterZipkinPackageVersion>1.2.0-rc1</OpenTelemetryExporterZipkinPackageVersion>
<OpenTelemetryInstrumentationAspNetCorePackageVersion>1.0.0-rc8</OpenTelemetryInstrumentationAspNetCorePackageVersion>
<OpenTelemetryInstrumentationHttpPackageVersion>1.0.0-rc8</OpenTelemetryInstrumentationHttpPackageVersion>
<OpenTelemetryContribInstrumentationEntityFrameworkCorePackageVersion>1.0.0-beta2</OpenTelemetryContribInstrumentationEntityFrameworkCorePackageVersion>
<QuartzNETPackageVersion>3.3.3</QuartzNETPackageVersion>
<StackExchangeRedisPackageVersion>2.0.593</StackExchangeRedisPackageVersion>
<SerilogPackageVersion>2.10.0</SerilogPackageVersion>

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}

16
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks.ClientProxies/LINGYUN.Abp.WebHooks.ClientProxies.csproj

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

10
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks.ClientProxies/LINGYUN/Abp/WebHooks/ClientProxies/AbpWebHooksClientProxiesModule.cs

@ -0,0 +1,10 @@
using LINGYUN.Abp.WebhooksManagement;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Webhooks.ClientProxies;
[DependsOn(typeof(AbpWebhooksModule))]
[DependsOn(typeof(WebhooksManagementHttpApiClientModule))]
public class AbpWebHooksClientProxiesModule : AbpModule
{
}

91
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks.ClientProxies/LINGYUN/Abp/WebHooks/ClientProxies/ClientProxiesWebhookPublisher.cs

@ -0,0 +1,91 @@
using LINGYUN.Abp.WebhooksManagement;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Webhooks.ClientProxies;
[Dependency(ReplaceServices = true)]
public class ClientProxiesWebhookPublisher : IWebhookPublisher, ITransientDependency
{
protected IWebhookPublishAppService PublishAppService { get; }
public ClientProxiesWebhookPublisher(
IWebhookPublishAppService publishAppService)
{
PublishAppService = publishAppService;
}
public async virtual Task PublishAsync(string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null)
{
var input = new WebhookPublishInput
{
WebhookName = webhookName,
Data = JsonConvert.SerializeObject(data),
SendExactSameData = sendExactSameData,
};
if (headers != null)
{
input.Header = new WebhooksHeaderInput
{
UseOnlyGivenHeaders = headers.UseOnlyGivenHeaders,
Headers = headers.Headers
};
}
await PublishAsync(input);
}
public async virtual Task PublishAsync(string webhookName, object data, Guid? tenantId, bool sendExactSameData = false, WebhookHeader headers = null)
{
var input = new WebhookPublishInput
{
WebhookName = webhookName,
Data = JsonConvert.SerializeObject(data),
SendExactSameData = sendExactSameData,
TenantIds = new List<Guid?>
{
tenantId
},
};
if (headers != null)
{
input.Header = new WebhooksHeaderInput
{
UseOnlyGivenHeaders = headers.UseOnlyGivenHeaders,
Headers = headers.Headers
};
}
await PublishAsync(input);
}
public async virtual Task PublishAsync(Guid?[] tenantIds, string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null)
{
var input = new WebhookPublishInput
{
WebhookName = webhookName,
Data = JsonConvert.SerializeObject(data),
SendExactSameData = sendExactSameData,
TenantIds = tenantIds.ToList(),
};
if (headers != null)
{
input.Header = new WebhooksHeaderInput
{
UseOnlyGivenHeaders = headers.UseOnlyGivenHeaders,
Headers = headers.Headers
};
}
await PublishAsync(input);
}
protected virtual async Task PublishAsync(WebhookPublishInput input)
{
await PublishAppService.PublishAsync(input);
}
}

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

54
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksModule.cs

@ -0,0 +1,54 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
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(AbpBackgroundJobsModule))]
[DependsOn(typeof(AbpFeaturesModule))]
[DependsOn(typeof(AbpGuidsModule))]
[DependsOn(typeof(AbpHttpClientModule))]
public class AbpWebhooksModule : AbpModule
{
internal const string WebhooksClient = "__Abp_Webhooks_HttpClient";
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var options = context.Services.ExecutePreConfiguredActions<AbpWebhooksOptions>();
context.Services.AddHttpClient(WebhooksClient, client =>
{
client.Timeout = options.TimeoutDuration;
});
}
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
{
if (typeof(WebhookDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
services.Configure<AbpWebhooksOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
}

35
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs

@ -0,0 +1,35 @@
using System;
using Volo.Abp.Collections;
namespace LINGYUN.Abp.Webhooks;
public class AbpWebhooksOptions
{
/// <summary>
/// 默认超时时间
/// </summary>
public TimeSpan TimeoutDuration { get; set; }
/// <summary>
/// 默认最大发送次数
/// </summary>
public int MaxSendAttemptCount { get; set; }
/// <summary>
/// 是否达到最大连续失败次数时自动取消订阅
/// </summary>
public bool IsAutomaticSubscriptionDeactivationEnabled { get; set; }
/// <summary>
/// 取消订阅前最大连续失败次数
/// </summary>
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>();
}
}

131
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/BackgroundWorker/WebhookSenderJob.cs

@ -0,0 +1,131 @@
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 IWebhookDefinitionManager _webhookDefinitionManager;
private readonly IWebhookSubscriptionManager _webhookSubscriptionManager;
private readonly IWebhookSendAttemptStore _webhookSendAttemptStore;
private readonly IWebhookSender _webhookSender;
private readonly AbpWebhooksOptions _options;
public WebhookSenderJob(
IUnitOfWorkManager unitOfWorkManager,
IWebhookDefinitionManager webhookDefinitionManager,
IWebhookSubscriptionManager webhookSubscriptionManager,
IWebhookSendAttemptStore webhookSendAttemptStore,
IWebhookSender webhookSender,
IOptions<AbpWebhooksOptions> options)
{
_unitOfWorkManager = unitOfWorkManager;
_webhookDefinitionManager = webhookDefinitionManager;
_webhookSubscriptionManager = webhookSubscriptionManager;
_webhookSendAttemptStore = webhookSendAttemptStore;
_webhookSender = webhookSender;
_options = options.Value;
}
public override async Task ExecuteAsync(WebhookSenderArgs args)
{
var webhookDefinition = _webhookDefinitionManager.Get(args.WebhookName);
if (webhookDefinition.TryOnce)
{
try
{
await SendWebhook(args, webhookDefinition);
}
catch (Exception e)
{
Logger.LogWarning("An error occured while sending webhook with try once.", e);
// ignored
}
}
else
{
await SendWebhook(args, webhookDefinition);
}
}
private async Task SendWebhook(WebhookSenderArgs args, WebhookDefinition webhookDefinition)
{
if (args.WebhookEventId == default)
{
return;
}
if (args.WebhookSubscriptionId == default)
{
return;
}
if (!webhookDefinition.TryOnce)
{
var sendAttemptCount = await _webhookSendAttemptStore.GetSendAttemptCountAsync(
args.TenantId,
args.WebhookEventId,
args.WebhookSubscriptionId
);
if ((webhookDefinition.MaxSendAttemptCount > 0 && sendAttemptCount > webhookDefinition.MaxSendAttemptCount) ||
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;
}
}
}
}

140
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookPublisher.cs

@ -0,0 +1,140 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Webhooks
{
public class DefaultWebhookPublisher : IWebhookPublisher, ITransientDependency
{
public IWebhookEventStore WebhookEventStore { get; set; }
private readonly ICurrentTenant _currentTenant;
private readonly IBackgroundJobManager _backgroundJobManager;
private readonly IWebhookSubscriptionManager _webhookSubscriptionManager;
public DefaultWebhookPublisher(
IWebhookSubscriptionManager webhookSubscriptionManager,
ICurrentTenant currentTenant,
IBackgroundJobManager backgroundJobManager)
{
_currentTenant = currentTenant;
_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);
}
protected virtual 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
{
WebhookName = webhookName,
Data = JsonConvert.SerializeObject(data),
TenantId = tenantId
};
var webhookId = await WebhookEventStore.InsertAndGetIdAsync(webhookInfo);
webhookInfo.Id = webhookId;
return webhookInfo;
}
}
}

129
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/DefaultWebhookSender.cs

@ -0,0 +1,129 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Webhooks
{
public class DefaultWebhookSender : IWebhookSender, ITransientDependency
{
public ILogger<DefaultWebhookSender> Logger { protected get; set; }
private readonly IWebhookManager _webhookManager;
private readonly IHttpClientFactory _httpClientFactory;
private const string FailedRequestDefaultContent = "Webhook Send Request Failed";
public DefaultWebhookSender(
IWebhookManager webhookManager,
IHttpClientFactory httpClientFactory)
{
_webhookManager = webhookManager;
_httpClientFactory = httpClientFactory;
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)
{
var client = _httpClientFactory.CreateClient(AbpWebhooksModule.WebhooksClient);
var response = await client.SendAsync(request);
var isSucceed = response.IsSuccessStatusCode;
var statusCode = response.StatusCode;
var content = await response.Content.ReadAsStringAsync();
return (isSucceed, statusCode, content);
}
}
}

21
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/Extensions/WebhookSubscriptionExtensions.cs

@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.Webhooks.Extensions
{
public static class WebhookSubscriptionExtensions
{
/// <summary>
/// checks if subscribed to given webhook
/// </summary>
/// <returns></returns>
public static bool IsSubscribed(this WebhookSubscriptionInfo webhookSubscription, string webhookName)
{
if (webhookSubscription.Webhooks.IsNullOrEmpty())
{
return false;
}
return webhookSubscription.Webhooks.Contains(webhookName);
}
}
}

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

@ -0,0 +1,16 @@
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Webhooks
{
public interface IWebhookDefinitionContext
{
WebhookGroupDefinition AddGroup(
[NotNull] string name,
ILocalizableString displayName = null);
WebhookGroupDefinition GetGroupOrNull(string name);
void RemoveGroup(string name);
}
}

37
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/IWebhookDefinitionManager.cs

@ -0,0 +1,37 @@
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>
/// Gets all webhook group definitions.
/// </summary>
/// <returns></returns>
IReadOnlyList<WebhookGroupDefinition> GetGroups();
/// <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<(int TotalCount, IReadOnlyCollection<WebhookSendAttempt> Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount,
int skipCount)
{
return Task.FromResult(ValueTuple.Create(0, 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);
}
}
}

67
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinition.cs

@ -0,0 +1,67 @@
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>
/// Tries to send webhook only one time without checking to send attempt count
/// </summary>
public bool TryOnce { get; set; }
/// <summary>
/// Defined maximum number of sending times
/// </summary>
public int MaxSendAttemptCount { get; set; }
/// <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}]";
}
}
}

55
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionContext.cs

@ -0,0 +1,55 @@
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, WebhookGroupDefinition> Groups { get; }
public WebhookDefinitionContext(Dictionary<string, WebhookGroupDefinition> webhooks)
{
Groups = webhooks;
}
public WebhookGroupDefinition AddGroup(
[NotNull] string name,
ILocalizableString displayName = null)
{
Check.NotNull(name, nameof(name));
if (Groups.ContainsKey(name))
{
throw new AbpException($"There is already an existing webhook group with name: {name}");
}
return Groups[name] = new WebhookGroupDefinition(name, displayName);
}
public WebhookGroupDefinition GetGroupOrNull([NotNull] string name)
{
Check.NotNull(name, nameof(name));
if (!Groups.ContainsKey(name))
{
return null;
}
return Groups[name];
}
public void RemoveGroup(string name)
{
Check.NotNull(name, nameof(name));
if (!Groups.ContainsKey(name))
{
throw new AbpException($"Undefined notification webhook group: '{name}'.");
}
Groups.Remove(name);
}
}
}

139
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs

@ -0,0 +1,139 @@
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;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.Webhooks
{
internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonDependency
{
protected IDictionary<string, WebhookGroupDefinition> WebhookGroupDefinitions => _lazyWebhookGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, WebhookGroupDefinition>> _lazyWebhookGroupDefinitions;
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;
_lazyWebhookGroupDefinitions = new Lazy<Dictionary<string, WebhookGroupDefinition>>(CreateWebhookGroupDefinitions);
_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 IReadOnlyList<WebhookGroupDefinition> GetGroups()
{
return WebhookGroupDefinitions.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>();
foreach (var groupDefinition in WebhookGroupDefinitions.Values)
{
foreach (var webhook in groupDefinition.Webhooks)
{
if (definitions.ContainsKey(webhook.Name))
{
throw new AbpException("Duplicate webhook name: " + webhook.Name);
}
definitions[webhook.Name] = webhook;
}
}
return definitions;
}
protected virtual Dictionary<string, WebhookGroupDefinition> CreateWebhookGroupDefinitions()
{
var definitions = new Dictionary<string, WebhookGroupDefinition>();
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; }
}
}

101
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookGroupDefinition.cs

@ -0,0 +1,101 @@
using JetBrains.Annotations;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Webhooks;
public class WebhookGroupDefinition
{
[NotNull]
public string Name { get; set; }
public Dictionary<string, object> Properties { get; }
private ILocalizableString _displayName;
public ILocalizableString DisplayName
{
get {
return _displayName;
}
set {
_displayName = value;
}
}
public IReadOnlyList<WebhookDefinition> Webhooks => _webhooks.ToImmutableList();
private readonly List<WebhookDefinition> _webhooks;
public object this[string name] {
get => Properties.GetOrDefault(name);
set => Properties[name] = value;
}
protected internal WebhookGroupDefinition(
string name,
ILocalizableString displayName = null)
{
Name = name;
DisplayName = displayName ?? new FixedLocalizableString(Name);
Properties = new Dictionary<string, object>();
_webhooks = new List<WebhookDefinition>();
}
public virtual WebhookDefinition AddWebhook(
string name,
ILocalizableString displayName = null,
ILocalizableString description = null)
{
if (Webhooks.Any(hook => hook.Name.Equals(name)))
{
throw new AbpException($"There is already an existing webhook with name: {name} in group {Name}");
}
var webhook = new WebhookDefinition(
name,
displayName,
description
);
_webhooks.Add(webhook);
return webhook;
}
public virtual void AddWebhooks(params WebhookDefinition[] webhooks)
{
foreach (var webhook in webhooks)
{
if (Webhooks.Any(hook => hook.Name.Equals(webhook.Name)))
{
throw new AbpException($"There is already an existing webhook with name: {webhook.Name} in group {Name}");
}
}
_webhooks.AddRange(webhooks);
}
[CanBeNull]
public WebhookDefinition GetWebhookOrNull([NotNull] string name)
{
Check.NotNull(name, nameof(name));
foreach (var webhook in Webhooks)
{
if (webhook.Name == name)
{
return webhook;
}
}
return null;
}
public override string ToString()
{
return $"[{nameof(WebhookGroupDefinition)} {Name}]";
}
}

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

80
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookManager.cs

@ -0,0 +1,80 @@
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
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 IWebhookSendAttemptStore WebhookSendAttemptStore { get; }
protected WebhookManager(
IWebhookSendAttemptStore webhookSendAttemptStore)
{
WebhookSendAttemptStore = webhookSendAttemptStore;
}
public virtual async Task<WebhookPayload> GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs)
{
var data = JsonConvert.SerializeObject(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 = JsonConvert.SerializeObject(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; }
}
}

62
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSenderArgs.cs

@ -0,0 +1,62 @@
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>
/// 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>();
}
}
}

172
aspnet-core/modules/webhooks/LINGYUN.Abp.WebHooks/LINGYUN/Abp/Webhooks/WebhookSubscriptionManager.cs

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.Webhooks
{
public class WebhookSubscriptionManager : IWebhookSubscriptionManager, ITransientDependency
{
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
{
await WebhookSubscriptionsStore.UpdateAsync(webhookSubscription);
}
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.Webhooks.Identity/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.Identity/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>

30
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN.Abp.Webhooks.Identity.csproj

@ -0,0 +1,30 @@
<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\Webhooks\Identity\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\Webhooks\Identity\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Users.Abstractions" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Identity.Domain.Shared" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Webhooks\LINGYUN.Abp.Webhooks.csproj" />
</ItemGroup>
</Project>

32
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/AbpWebhooksIdentityModule.cs

@ -0,0 +1,32 @@
using Volo.Abp.Domain;
using Volo.Abp.EventBus;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Users;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.Webhooks.Identity;
[DependsOn(typeof(AbpDddDomainModule))]
[DependsOn(typeof(AbpEventBusModule))]
[DependsOn(typeof(AbpUsersAbstractionModule))]
[DependsOn(typeof(AbpIdentityDomainSharedModule))]
public class AbpWebhooksIdentityModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpWebhooksIdentityModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<IdentityResource>()
.AddVirtualJson("/LINGYUN/Abp/Webhooks/Identity/Localization/Resources");
});
}
}

13
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityRoleNameChangedWto.cs

@ -0,0 +1,13 @@
using System;
namespace LINGYUN.Abp.Webhooks.Identity;
[Serializable]
public class IdentityRoleNameChangedWto
{
public Guid Id { get; set; }
public string Name { get; set; }
public string OldName { get; set; }
}

63
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityRoleWebHooker.cs

@ -0,0 +1,63 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
namespace LINGYUN.Abp.Webhooks.Identity;
public class IdentityRoleWebhooker :
IDistributedEventHandler<EntityCreatedEto<IdentityRoleEto>>,
IDistributedEventHandler<EntityUpdatedEto<IdentityRoleEto>>,
IDistributedEventHandler<EntityDeletedEto<IdentityRoleEto>>,
IDistributedEventHandler<IdentityRoleNameChangedEto>,
ITransientDependency
{
private readonly IWebhookPublisher _webhookPublisher;
public IdentityRoleWebhooker(
IWebhookPublisher webhookPublisher)
{
_webhookPublisher = webhookPublisher;
}
public async virtual Task HandleEventAsync(EntityCreatedEto<IdentityRoleEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityRole.Create, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<IdentityRoleEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityRole.Update, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityDeletedEto<IdentityRoleEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityRole.Delete, eventData.Entity);
}
public async virtual Task HandleEventAsync(IdentityRoleNameChangedEto eventData)
{
await _webhookPublisher.PublishAsync(
IdentityWebhookNames.IdentityRole.ChangeName,
new IdentityRoleNameChangedWto
{
Id = eventData.Id,
Name = eventData.Name,
OldName = eventData.OldName,
},
eventData.TenantId);
}
protected async virtual Task PublishAsync(string webhookName, IdentityRoleEto eto)
{
await _webhookPublisher.PublishAsync(
webhookName,
new IdentityRoleWto
{
Id = eto.Id,
Name = eto.Name
},
eto.TenantId);
}
}

11
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityRoleWto.cs

@ -0,0 +1,11 @@
using System;
namespace LINGYUN.Abp.Webhooks.Identity;
[Serializable]
public class IdentityRoleWto
{
public Guid Id { get; set; }
public string Name { get; set; }
}

55
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityUserWebHooker.cs

@ -0,0 +1,55 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Users;
namespace LINGYUN.Abp.Webhooks.Identity;
public class IdentityUserWebhooker :
IDistributedEventHandler<EntityCreatedEto<UserEto>>,
IDistributedEventHandler<EntityUpdatedEto<UserEto>>,
IDistributedEventHandler<EntityDeletedEto<UserEto>>,
ITransientDependency
{
private readonly IWebhookPublisher _webhookPublisher;
public IdentityUserWebhooker(
IWebhookPublisher webhookPublisher)
{
_webhookPublisher = webhookPublisher;
}
public async virtual Task HandleEventAsync(EntityCreatedEto<UserEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityUser.Create, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<UserEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityUser.Update, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityDeletedEto<UserEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityUser.Delete, eventData.Entity);
}
protected async virtual Task PublishAsync(string webhookName, UserEto eto)
{
await _webhookPublisher.PublishAsync(
webhookName,
new IdentityUserWto
{
Id = eto.Id,
Name = eto.Name,
Email = eto.Email,
EmailConfirmed = eto.EmailConfirmed,
UserName = eto.UserName,
PhoneNumber = eto.PhoneNumber,
PhoneNumberConfirmed = eto.PhoneNumberConfirmed,
Surname = eto.Surname,
},
eto.TenantId);
}
}

23
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityUserWto.cs

@ -0,0 +1,23 @@
using System;
namespace LINGYUN.Abp.Webhooks.Identity;
[Serializable]
public class IdentityUserWto
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
}

64
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityWebhookDefinitionProvider.cs

@ -0,0 +1,64 @@
using Volo.Abp.Identity.Localization;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Webhooks.Identity;
public class IdentityWebhookDefinitionProvider : WebhookDefinitionProvider
{
public override void Define(IWebhookDefinitionContext context)
{
var identityGroup = context.AddGroup(
IdentityWebhookNames.GroupName,
L("Webhooks:Identity"));
identityGroup.AddWebhooks(CreateIdentityRoleWebhooks());
identityGroup.AddWebhooks(CreateIdentityUserWebhooks());
}
protected virtual WebhookDefinition[] CreateIdentityRoleWebhooks()
{
return new[]
{
new WebhookDefinition(
IdentityWebhookNames.IdentityRole.Create,
L("Webhooks:CreateRole"),
L("Webhooks:CreateRoleDesc")),
new WebhookDefinition(
IdentityWebhookNames.IdentityRole.Update,
L("Webhooks:UpdateRole"),
L("Webhooks:UpdateRoleDesc")),
new WebhookDefinition(
IdentityWebhookNames.IdentityRole.Delete,
L("Webhooks:DeleteRole"),
L("Webhooks:DeleteRoleDesc")),
new WebhookDefinition(
IdentityWebhookNames.IdentityRole.ChangeName,
L("Webhooks:ChangeRoleName"),
L("Webhooks:ChangeRoleNameDesc")),
};
}
protected virtual WebhookDefinition[] CreateIdentityUserWebhooks()
{
return new[]
{
new WebhookDefinition(
IdentityWebhookNames.IdentityUser.Create,
L("Webhooks:CreateUser"),
L("Webhooks:CreateUserDesc")),
new WebhookDefinition(
IdentityWebhookNames.IdentityUser.Update,
L("Webhooks:UpdateUser"),
L("Webhooks:UpdateUserDesc")),
new WebhookDefinition(
IdentityWebhookNames.IdentityUser.Delete,
L("Webhooks:DeleteUser"),
L("Webhooks:DeleteUserDesc")),
};
}
private static ILocalizableString L(string name)
{
return LocalizableString.Create<IdentityResource>(name);
}
}

22
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/IdentityWebhookNames.cs

@ -0,0 +1,22 @@
namespace LINGYUN.Abp.Webhooks.Identity;
public static class IdentityWebhookNames
{
public const string GroupName = "abp.webhooks.identity";
public static class IdentityRole
{
public const string Prefix = GroupName + ".roles";
public const string Create = Prefix + ".create";
public const string Update = Prefix + ".update";
public const string Delete = Prefix + ".delete";
public const string ChangeName = Prefix + ".change_name";
}
public static class IdentityUser
{
public const string Prefix = GroupName + ".users";
public const string Create = Prefix + ".create";
public const string Update = Prefix + ".update";
public const string Delete = Prefix + ".delete";
}
}

20
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/Localization/Resources/en.json

@ -0,0 +1,20 @@
{
"culture": "en",
"texts": {
"Webhooks:Identity": "Identity",
"Webhooks:CreateRole": "Create Role",
"Webhooks:CreateRoleDesc": "A new role has been created",
"Webhooks:UpdateRole": "Update Role",
"Webhooks:UpdateRoleDesc": "A role has changed",
"Webhooks:DeleteRole": "Delete Role",
"Webhooks:DeleteRoleDesc": "Deleted Role",
"Webhooks:ChangeRoleName": "Change Role Name",
"Webhooks:ChangeRoleNameDesc": "The name of a role was changed",
"Webhooks:CreateUser": "Create User",
"Webhooks:CreateUserDesc": "A new user has been created",
"Webhooks:UpdateUser": "Update User",
"Webhooks:UpdateUserDesc": "A user has been changed",
"Webhooks:DeleteUser": "Delete User",
"Webhooks:DeleteUserDesc": "Deleted User"
}
}

20
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/Localization/Resources/zh-Hans.json

@ -0,0 +1,20 @@
{
"culture": "zh-Hans",
"texts": {
"Webhooks:Identity": "身份认证",
"Webhooks:CreateRole": "创建角色",
"Webhooks:CreateRoleDesc": "一个新角色已创建",
"Webhooks:UpdateRole": "编辑角色",
"Webhooks:UpdateRoleDesc": "一个角色属性已变更",
"Webhooks:DeleteRole": "删除角色",
"Webhooks:DeleteRoleDesc": "已删除角色",
"Webhooks:ChangeRoleName": "改变角色名称",
"Webhooks:ChangeRoleNameDesc": "一个角色的名称被更改",
"Webhooks:CreateUser": "创建用户",
"Webhooks:CreateUserDesc": "一个新用户已创建",
"Webhooks:UpdateUser": "编辑用户",
"Webhooks:UpdateUserDesc": "一个用户属性已变更",
"Webhooks:DeleteUser": "删除用户",
"Webhooks:DeleteUserDesc": "已删除用户"
}
}

49
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/OrganizationUnitWebHooker.cs

@ -0,0 +1,49 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
namespace LINGYUN.Abp.Webhooks.Identity;
public class OrganizationUnitWebhooker :
IDistributedEventHandler<EntityCreatedEto<OrganizationUnitEto>>,
IDistributedEventHandler<EntityUpdatedEto<OrganizationUnitEto>>,
IDistributedEventHandler<EntityDeletedEto<OrganizationUnitEto>>,
ITransientDependency
{
private readonly IWebhookPublisher _webhookPublisher;
public OrganizationUnitWebhooker(
IWebhookPublisher webhookPublisher)
{
_webhookPublisher = webhookPublisher;
}
public async virtual Task HandleEventAsync(EntityCreatedEto<OrganizationUnitEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityRole.Create, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<OrganizationUnitEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityRole.Update, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityDeletedEto<OrganizationUnitEto> eventData)
{
await PublishAsync(IdentityWebhookNames.IdentityRole.Delete, eventData.Entity);
}
protected async virtual Task PublishAsync(string webhookName, OrganizationUnitEto eto)
{
await _webhookPublisher.PublishAsync(
webhookName,
new OrganizationUnitWto
{
Id = eto.Id,
DisplayName = eto.DisplayName
},
eto.TenantId);
}
}

13
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Identity/LINGYUN/Abp/Webhooks/Identity/OrganizationUnitWto.cs

@ -0,0 +1,13 @@
using System;
namespace LINGYUN.Abp.Webhooks.Identity;
[Serializable]
public class OrganizationUnitWto
{
public Guid Id { get; set; }
public string Code { get; set; }
public string DisplayName { get; set; }
}

3
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/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.Saas/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>

30
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN.Abp.Webhooks.Saas.csproj

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\Webhooks\Saas\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\Webhooks\Saas\Localization\Resources\en.json" />
<None Remove="LINGYUN\Abp\Webhooks\Saas\Localization\Resources\zh-Hans.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Webhooks\LINGYUN.Abp.Webhooks.csproj" />
<ProjectReference Include="..\..\saas\LINGYUN.Abp.Saas.Domain.Shared\LINGYUN.Abp.Saas.Domain.Shared.csproj" />
</ItemGroup>
</Project>

30
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/AbpWebhooksSaasModule.cs

@ -0,0 +1,30 @@
using LINGYUN.Abp.Saas;
using LINGYUN.Abp.Saas.Localization;
using Volo.Abp.Domain;
using Volo.Abp.EventBus;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.Webhooks.Saas;
[DependsOn(typeof(AbpDddDomainModule))]
[DependsOn(typeof(AbpEventBusModule))]
[DependsOn(typeof(AbpSaasDomainSharedModule))]
public class AbpWebhooksSaasModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpWebhooksSaasModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<AbpSaasResource>()
.AddVirtualJson("/LINGYUN/Abp/Webhooks/Saas/Localization/Resources");
});
}
}

48
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/EditionWebhooker.cs

@ -0,0 +1,48 @@
using LINGYUN.Abp.Saas.Editions;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
namespace LINGYUN.Abp.Webhooks.Saas;
public class EditionWebhooker :
IDistributedEventHandler<EntityCreatedEto<EditionEto>>,
IDistributedEventHandler<EntityUpdatedEto<EditionEto>>,
IDistributedEventHandler<EntityDeletedEto<EditionEto>>,
ITransientDependency
{
private readonly IWebhookPublisher _webhookPublisher;
public EditionWebhooker(
IWebhookPublisher webhookPublisher)
{
_webhookPublisher = webhookPublisher;
}
public async virtual Task HandleEventAsync(EntityCreatedEto<EditionEto> eventData)
{
await PublishAsync(SaasWebhookNames.Edition.Create, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<EditionEto> eventData)
{
await PublishAsync(SaasWebhookNames.Edition.Update, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityDeletedEto<EditionEto> eventData)
{
await PublishAsync(SaasWebhookNames.Edition.Delete, eventData.Entity);
}
protected async virtual Task PublishAsync(string webhookName, EditionEto eto)
{
await _webhookPublisher.PublishAsync(
webhookName,
new EditionWto
{
Id = eto.Id,
DisplayName = eto.DisplayName
});
}
}

11
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/EditionWto.cs

@ -0,0 +1,11 @@
using System;
namespace LINGYUN.Abp.Webhooks.Saas;
[Serializable]
public class EditionWto
{
public Guid Id { get; set; }
public string DisplayName { get; set; }
}

18
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/Localization/Resources/en.json

@ -0,0 +1,18 @@
{
"culture": "en",
"texts": {
"Webhooks:Saas": "Saas",
"Webhooks:CreateEdition": "Create Edition",
"Webhooks:CreateEditionDesc": "A new Edition has been created",
"Webhooks:UpdateEdition": "Update Edition",
"Webhooks:UpdateEditionDesc": "A Edition has changed",
"Webhooks:DeleteEdition": "Delete Edition",
"Webhooks:DeleteEditionDesc": "Deleted Edition",
"Webhooks:CreateTenant": "Create Tenant",
"Webhooks:CreateTenantDesc": "A new Tenant has been created",
"Webhooks:UpdateTenant": "Update Tenant",
"Webhooks:UpdateTenantDesc": "A Tenant has been changed",
"Webhooks:DeleteTenant": "Delete Tenant",
"Webhooks:DeleteTenantDesc": "Deleted Tenant"
}
}

18
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/Localization/Resources/zh-Hans.json

@ -0,0 +1,18 @@
{
"culture": "zh-Hans",
"texts": {
"Webhooks:Saas": "Saas",
"Webhooks:CreateEdition": "创建版本",
"Webhooks:CreateEditionDesc": "一个新版本已创建",
"Webhooks:UpdateEdition": "编辑版本",
"Webhooks:UpdateEditionDesc": "一个版本属性已变更",
"Webhooks:DeleteEdition": "删除版本",
"Webhooks:DeleteEditionDesc": "已删除版本",
"Webhooks:CreateTenant": "创建租户",
"Webhooks:CreateTenantDesc": "一个新租户已创建",
"Webhooks:UpdateTenant": "编辑租户",
"Webhooks:UpdateTenantDesc": "一个租户属性已变更",
"Webhooks:DeleteTenant": "删除租户",
"Webhooks:DeleteTenantDesc": "已删除租户"
}
}

60
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/SaasWebhookDefinitionProvider.cs

@ -0,0 +1,60 @@
using LINGYUN.Abp.Saas.Localization;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Webhooks.Saas;
public class SaasWebhookDefinitionProvider : WebhookDefinitionProvider
{
public override void Define(IWebhookDefinitionContext context)
{
var saasGroup = context.AddGroup(
SaasWebhookNames.GroupName,
L("Webhooks:Saas"));
saasGroup.AddWebhooks(CreateEditionWebhooks());
saasGroup.AddWebhooks(CreateTenantWebhooks());
}
protected virtual WebhookDefinition[] CreateEditionWebhooks()
{
return new[]
{
new WebhookDefinition(
SaasWebhookNames.Edition.Create,
L("Webhooks:CreateEdition"),
L("Webhooks:CreateEditionDesc")),
new WebhookDefinition(
SaasWebhookNames.Edition.Update,
L("Webhooks:UpdateEdition"),
L("Webhooks:UpdateEditionDesc")),
new WebhookDefinition(
SaasWebhookNames.Edition.Delete,
L("Webhooks:DeleteEdition"),
L("Webhooks:DeleteEditionDesc")),
};
}
protected virtual WebhookDefinition[] CreateTenantWebhooks()
{
return new[]
{
new WebhookDefinition(
SaasWebhookNames.Tenant.Create,
L("Webhooks:CreateTenant"),
L("Webhooks:CreateTenantDesc")),
new WebhookDefinition(
SaasWebhookNames.Tenant.Update,
L("Webhooks:UpdateTenant"),
L("Webhooks:UpdateTenantDesc")),
new WebhookDefinition(
SaasWebhookNames.Tenant.Delete,
L("Webhooks:DeleteTenant"),
L("Webhooks:DeleteTenantDesc")),
};
}
private static ILocalizableString L(string name)
{
return LocalizableString.Create<AbpSaasResource>(name);
}
}

21
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/SaasWebhookNames.cs

@ -0,0 +1,21 @@
namespace LINGYUN.Abp.Webhooks.Saas;
public static class SaasWebhookNames
{
public const string GroupName = "abp.webhooks.saas";
public static class Edition
{
public const string Prefix = GroupName + ".editions";
public const string Create = Prefix + ".create";
public const string Update = Prefix + ".update";
public const string Delete = Prefix + ".delete";
}
public static class Tenant
{
public const string Prefix = GroupName + ".tenants";
public const string Create = Prefix + ".create";
public const string Update = Prefix + ".update";
public const string Delete = Prefix + ".delete";
}
}

48
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/TenantWebhooker.cs

@ -0,0 +1,48 @@
using LINGYUN.Abp.Saas.Tenants;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
namespace LINGYUN.Abp.Webhooks.Saas;
public class TenantWebhooker :
IDistributedEventHandler<EntityCreatedEto<TenantEto>>,
IDistributedEventHandler<EntityUpdatedEto<TenantEto>>,
IDistributedEventHandler<EntityDeletedEto<TenantEto>>,
ITransientDependency
{
private readonly IWebhookPublisher _webhookPublisher;
public TenantWebhooker(
IWebhookPublisher webhookPublisher)
{
_webhookPublisher = webhookPublisher;
}
public async virtual Task HandleEventAsync(EntityCreatedEto<TenantEto> eventData)
{
await PublishAsync(SaasWebhookNames.Tenant.Create, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityUpdatedEto<TenantEto> eventData)
{
await PublishAsync(SaasWebhookNames.Tenant.Update, eventData.Entity);
}
public async virtual Task HandleEventAsync(EntityDeletedEto<TenantEto> eventData)
{
await PublishAsync(SaasWebhookNames.Tenant.Delete, eventData.Entity);
}
protected async virtual Task PublishAsync(string webhookName, TenantEto eto)
{
await _webhookPublisher.PublishAsync(
webhookName,
new TenantWto
{
Id = eto.Id,
Name = eto.Name
});
}
}

12
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Saas/LINGYUN/Abp/Webhooks/Saas/TenantWto.cs

@ -0,0 +1,12 @@
using System;
namespace LINGYUN.Abp.Webhooks.Saas;
[Serializable]
public class TenantWto
{
public Guid Id { get; set; }
public string Name { get; set; }
}

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>

62
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissionDefinitionProvider.cs

@ -0,0 +1,62 @@
using LINGYUN.Abp.WebhooksManagement.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.WebhooksManagement.Authorization;
public class WebhooksManagementPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var group = context.AddGroup(
WebhooksManagementPermissions.GroupName,
L("Permission:WebhooksManagement"),
MultiTenancySides.Host);
var subscription = group.AddPermission(
WebhooksManagementPermissions.WebhookSubscription.Default,
L("Permission:Subscriptions"),
MultiTenancySides.Host)
.WithProviders(ClientPermissionValueProvider.ProviderName);
subscription.AddChild(
WebhooksManagementPermissions.WebhookSubscription.Create,
L("Permission:Create"),
MultiTenancySides.Host)
.WithProviders(ClientPermissionValueProvider.ProviderName);
subscription.AddChild(
WebhooksManagementPermissions.WebhookSubscription.Update,
L("Permission:Update"),
MultiTenancySides.Host)
.WithProviders(ClientPermissionValueProvider.ProviderName);
subscription.AddChild(
WebhooksManagementPermissions.WebhookSubscription.Delete,
L("Permission:Delete"),
MultiTenancySides.Host)
.WithProviders(ClientPermissionValueProvider.ProviderName);
var sendAttempts = group.AddPermission(
WebhooksManagementPermissions.WebhooksSendAttempts.Default,
L("Permission:SendAttempts"),
MultiTenancySides.Host);
sendAttempts.AddChild(
WebhooksManagementPermissions.WebhooksSendAttempts.Resend,
L("Permission:Resend"),
MultiTenancySides.Host);
group.AddPermission(
WebhooksManagementPermissions.Publish,
L("Permission:Publish"))
.WithProviders(ClientPermissionValueProvider.ProviderName);
group.AddPermission(
WebhooksManagementPermissions.ManageSettings,
L("Permission:ManageSettings"),
MultiTenancySides.Host);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<WebhooksManagementResource>(name);
}
}

27
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/Authorization/WebhooksManagementPermissions.cs

@ -0,0 +1,27 @@
namespace LINGYUN.Abp.WebhooksManagement.Authorization;
public static class WebhooksManagementPermissions
{
public const string GroupName = "AbpWebhooks";
/// <summary>
/// 授权允许发布Webhooks事件, 建议客户端授权
/// </summary>
public const string Publish = GroupName + ".Publish";
public const string ManageSettings = GroupName + ".ManageSettings";
public static class WebhookSubscription
{
public const string Default = GroupName + ".Subscriptions";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public static class WebhooksSendAttempts
{
public const string Default = GroupName + ".SendAttempts";
public const string Resend = Default + ".Resend";
}
}

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

9
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/IWebhookPublishAppService.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.WebhooksManagement;
public interface IWebhookPublishAppService : IApplicationService
{
Task PublishAsync(WebhookPublishInput input);
}

15
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/IWebhookSendRecordAppService.cs

@ -0,0 +1,15 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.WebhooksManagement;
public interface IWebhookSendRecordAppService : IApplicationService
{
Task<WebhookSendRecordDto> GetAsync(Guid id);
Task ResendAsync(Guid id);
Task<PagedResultDto<WebhookSendRecordDto>> GetListAsync(WebhookSendRecordGetListInput input);
}

17
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/IWebhookSubscriptionAppService.cs

@ -0,0 +1,17 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.WebhooksManagement;
public interface IWebhookSubscriptionAppService :
ICrudAppService<
WebhookSubscriptionDto,
Guid,
WebhookSubscriptionGetListInput,
WebhookSubscriptionCreateInput,
WebhookSubscriptionUpdateInput>
{
Task<ListResultDto<WebhookAvailableGroupDto>> GetAllAvailableWebhooksAsync();
}

8
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookAvailableDto.cs

@ -0,0 +1,8 @@
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookAvailableDto
{
public string Name { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
}

10
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookAvailableGroupDto.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookAvailableGroupDto
{
public string Name { get; set; }
public string DisplayName { get; set; }
public List<WebhookAvailableDto> Webhooks { get; set; } = new List<WebhookAvailableDto>();
}

12
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookEventRecordDto.cs

@ -0,0 +1,12 @@
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookEventRecordDto : EntityDto<Guid>
{
public Guid? TenantId { get; set; }
public string WebhookName { get; set; }
public string Data { get; set; }
public DateTime CreationTime { get; set; }
}

30
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookPublishInput.cs

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookPublishInput
{
[Required]
[DynamicStringLength(typeof(WebhookEventRecordConsts), nameof(WebhookEventRecordConsts.MaxWebhookNameLength))]
public string WebhookName { get; set; }
[Required]
[DynamicStringLength(typeof(WebhookEventRecordConsts), nameof(WebhookEventRecordConsts.MaxDataLength))]
public string Data { get; set; }
public bool SendExactSameData { get; set; }
public WebhooksHeaderInput Header { get; set; } = new WebhooksHeaderInput();
public List<Guid?> TenantIds { get; set; } = new List<Guid?>();
}
public class WebhooksHeaderInput
{
public bool UseOnlyGivenHeaders { get; set; }
public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
}

24
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordDto.cs

@ -0,0 +1,24 @@
using System;
using System.Net;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookSendRecordDto : EntityDto<Guid>
{
public Guid? TenantId { get; set; }
public Guid WebhookEventId { get; set; }
public Guid WebhookSubscriptionId { get; set; }
public string Response { get; set; }
public HttpStatusCode? ResponseStatusCode { get; set; }
public DateTime CreationTime { get; set; }
public DateTime? LastModificationTime { get; set; }
public WebhookEventRecordDto WebhookEvent { get; set; } = new WebhookEventRecordDto();
}

20
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordGetListInput.cs

@ -0,0 +1,20 @@
using System;
using System.Net;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookSendRecordGetListInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public Guid? WebhookEventId { get; set; }
public Guid? SubscriptionId { get; set; }
public HttpStatusCode? ResponseStatusCode { get; set; }
public DateTime? BeginCreationTime { get; set; }
public DateTime? EndCreationTime { get; set; }
}

32
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionCreateOrUpdateInput.cs

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookSubscriptionCreateInput : WebhookSubscriptionCreateOrUpdateInput
{
}
public class WebhookSubscriptionUpdateInput : WebhookSubscriptionCreateOrUpdateInput
{
}
public abstract class WebhookSubscriptionCreateOrUpdateInput
{
[Required]
[DynamicStringLength(typeof(WebhookSubscriptionConsts), nameof(WebhookSubscriptionConsts.MaxWebhookUriLength))]
public string WebhookUri { get; set; }
[Required]
[DynamicStringLength(typeof(WebhookSubscriptionConsts), nameof(WebhookSubscriptionConsts.MaxSecretLength))]
public string Secret { get; set; }
public bool IsActive { get; set; }
public List<string> Webhooks { get; set; } = new List<string>();
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
}

15
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionDto.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookSubscriptionDto : CreationAuditedEntityDto<Guid>
{
public Guid? TenantId { get; set; }
public string WebhookUri { get; set; }
public string Secret { get; set; }
public bool IsActive { get; set; }
public List<string> Webhooks { get; set; } = new List<string>();
public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
}

26
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application.Contracts/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionGetListInput.cs

@ -0,0 +1,26 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhookSubscriptionGetListInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public Guid? TenantId { get; set; }
[DynamicStringLength(typeof(WebhookSubscriptionConsts), nameof(WebhookSubscriptionConsts.MaxWebhookUriLength))]
public string WebhookUri { get; set; }
[DynamicStringLength(typeof(WebhookSubscriptionConsts), nameof(WebhookSubscriptionConsts.MaxSecretLength))]
public string Secret { get; set; }
public bool? IsActive { get; set; }
public string Webhooks { get; set; }
public DateTime? BeginCreationTime { get; set; }
public DateTime? EndCreationTime { get; set; }
}

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 = "webhooks-management";
}

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>

44
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/Extensions/WebhookSubscriptionExtensions.cs

@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System.Linq;
namespace LINGYUN.Abp.WebhooksManagement.Extensions
{
public static class WebhookSubscriptionExtensions
{
public static WebhookSubscriptionDto ToWebhookSubscriptionDto(this WebhookSubscription webhookSubscription)
{
return new WebhookSubscriptionDto
{
Id = webhookSubscription.Id,
TenantId = webhookSubscription.TenantId,
IsActive = webhookSubscription.IsActive,
Secret = webhookSubscription.Secret,
WebhookUri = webhookSubscription.WebhookUri,
Webhooks = webhookSubscription.GetSubscribedWebhooks(),
Headers = webhookSubscription.GetWebhookHeaders(),
CreationTime = webhookSubscription.CreationTime,
CreatorId = webhookSubscription.CreatorId
};
}
public static string ToSubscribedWebhooksString(this WebhookSubscriptionUpdateInput webhookSubscription)
{
if (webhookSubscription.Webhooks.Any())
{
return JsonConvert.SerializeObject(webhookSubscription.Webhooks);
}
return null;
}
public static string ToWebhookHeadersString(this WebhookSubscriptionUpdateInput webhookSubscription)
{
if (webhookSubscription.Headers.Any())
{
return JsonConvert.SerializeObject(webhookSubscription.Headers);
}
return null;
}
}
}

45
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhookPublishAppService.cs

@ -0,0 +1,45 @@
using LINGYUN.Abp.Webhooks;
using LINGYUN.Abp.WebhooksManagement.Authorization;
using Microsoft.AspNetCore.Authorization;
using Newtonsoft.Json;
using System.Linq;
using System.Threading.Tasks;
namespace LINGYUN.Abp.WebhooksManagement;
[Authorize(WebhooksManagementPermissions.Publish)]
public class WebhookPublishAppService : WebhooksManagementAppServiceBase, IWebhookPublishAppService
{
protected IWebhookPublisher InnerPublisher { get; }
public WebhookPublishAppService(IWebhookPublisher innerPublisher)
{
InnerPublisher = innerPublisher;
}
public async virtual Task PublishAsync(WebhookPublishInput input)
{
var webhookHeader = new WebhookHeader
{
UseOnlyGivenHeaders = input.Header.UseOnlyGivenHeaders,
Headers = input.Header.Headers,
};
var inputData = JsonConvert.DeserializeObject(input.Data);
if (input.TenantIds.Any())
{
await InnerPublisher.PublishAsync(
input.TenantIds.ToArray(),
input.WebhookName,
inputData,
input.SendExactSameData,
webhookHeader);
return;
}
await InnerPublisher.PublishAsync(
input.WebhookName,
inputData,
input.SendExactSameData,
webhookHeader);
}
}

74
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhookSendRecordAppService.cs

@ -0,0 +1,74 @@
using LINGYUN.Abp.Webhooks;
using LINGYUN.Abp.WebhooksManagement.Authorization;
using LINGYUN.Abp.WebhooksManagement.Extensions;
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.BackgroundJobs;
namespace LINGYUN.Abp.WebhooksManagement;
[Authorize(WebhooksManagementPermissions.WebhooksSendAttempts.Default)]
public class WebhookSendRecordAppService : WebhooksManagementAppServiceBase, IWebhookSendRecordAppService
{
protected IBackgroundJobManager BackgroundJobManager => LazyServiceProvider.LazyGetRequiredService<IBackgroundJobManager>();
protected IWebhookEventRecordRepository EventRepository => LazyServiceProvider.LazyGetRequiredService<IWebhookEventRecordRepository>();
protected IWebhookSubscriptionRepository SubscriptionRepository => LazyServiceProvider.LazyGetRequiredService<IWebhookSubscriptionRepository>();
protected IWebhookSendRecordRepository RecordRepository { get; }
public WebhookSendRecordAppService(
IWebhookSendRecordRepository recordRepository)
{
RecordRepository = recordRepository;
}
public async virtual Task<WebhookSendRecordDto> GetAsync(Guid id)
{
var sendRecord = await RecordRepository.GetAsync(id);
return ObjectMapper.Map<WebhookSendRecord, WebhookSendRecordDto>(sendRecord);
}
public async virtual Task<PagedResultDto<WebhookSendRecordDto>> GetListAsync(WebhookSendRecordGetListInput input)
{
var filter = new WebhookSendRecordFilter
{
SubscriptionId = input.SubscriptionId,
ResponseStatusCode = input.ResponseStatusCode,
BeginCreationTime = input.BeginCreationTime,
EndCreationTime = input.EndCreationTime,
WebhookEventId = input.WebhookEventId,
Filter = input.Filter
};
var totalCount = await RecordRepository.GetCountAsync(filter);
var sendRecords = await RecordRepository.GetListAsync(filter,
input.Sorting, input.MaxResultCount, input.SkipCount);
return new PagedResultDto<WebhookSendRecordDto>(totalCount,
ObjectMapper.Map<List<WebhookSendRecord>, List<WebhookSendRecordDto>>(sendRecords));
}
[Authorize(WebhooksManagementPermissions.WebhooksSendAttempts.Resend)]
public async virtual Task ResendAsync(Guid id)
{
var sendRecord = await RecordRepository.GetAsync(id);
var sendEvent = await EventRepository.GetAsync(sendRecord.WebhookEventId);
var subscription = await SubscriptionRepository.GetAsync(sendRecord.WebhookSubscriptionId);
await BackgroundJobManager.EnqueueAsync(new WebhookSenderArgs
{
TenantId = CurrentTenant.Id,
WebhookSubscriptionId = sendRecord.WebhookSubscriptionId,
WebhookEventId = sendRecord.WebhookEventId,
WebhookName = sendEvent.WebhookName,
WebhookUri = subscription.WebhookUri,
Data = sendEvent.Data,
Headers = subscription.GetWebhookHeaders(),
Secret = subscription.Secret,
});
}
}

150
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhookSubscriptionAppService.cs

@ -0,0 +1,150 @@
using LINGYUN.Abp.Webhooks;
using LINGYUN.Abp.WebhooksManagement.Authorization;
using LINGYUN.Abp.WebhooksManagement.Extensions;
using Microsoft.AspNetCore.Authorization;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.WebhooksManagement;
[Authorize(WebhooksManagementPermissions.WebhookSubscription.Default)]
public class WebhookSubscriptionAppService : WebhooksManagementAppServiceBase, IWebhookSubscriptionAppService
{
protected IWebhookDefinitionManager WebhookDefinitionManager { get; }
protected IWebhookSubscriptionRepository SubscriptionRepository { get; }
public WebhookSubscriptionAppService(
IWebhookDefinitionManager webhookDefinitionManager,
IWebhookSubscriptionRepository subscriptionRepository)
{
WebhookDefinitionManager = webhookDefinitionManager;
SubscriptionRepository = subscriptionRepository;
}
[Authorize(WebhooksManagementPermissions.WebhookSubscription.Create)]
public async virtual Task<WebhookSubscriptionDto> CreateAsync(WebhookSubscriptionCreateInput input)
{
await CheckSubscribedAsync(input);
var subscription = new WebhookSubscription(
GuidGenerator.Create(),
input.WebhookUri,
input.Secret,
JsonConvert.SerializeObject(input.Webhooks),
JsonConvert.SerializeObject(input.Headers),
CurrentTenant.Id);
await SubscriptionRepository.InsertAsync(subscription);
await CurrentUnitOfWork.SaveChangesAsync();
return subscription.ToWebhookSubscriptionDto();
}
[Authorize(WebhooksManagementPermissions.WebhookSubscription.Delete)]
public virtual Task DeleteAsync(Guid id)
{
return SubscriptionRepository.DeleteAsync(id);
}
public async virtual Task<WebhookSubscriptionDto> GetAsync(Guid id)
{
var subscription = await SubscriptionRepository.GetAsync(id);
return subscription.ToWebhookSubscriptionDto();
}
public async virtual Task<PagedResultDto<WebhookSubscriptionDto>> GetListAsync(WebhookSubscriptionGetListInput input)
{
var filter = new WebhookSubscriptionFilter
{
Filter = input.Filter,
BeginCreationTime = input.BeginCreationTime,
EndCreationTime = input.EndCreationTime,
IsActive = input.IsActive,
Secret = input.Secret,
TenantId = input.TenantId,
Webhooks = input.Webhooks,
WebhookUri = input.WebhookUri
};
var totalCount = await SubscriptionRepository.GetCountAsync(filter);
var subscriptions = await SubscriptionRepository.GetListAsync(filter,
input.Sorting, input.MaxResultCount, input.SkipCount);
return new PagedResultDto<WebhookSubscriptionDto>(totalCount,
subscriptions.Select(subscription => subscription.ToWebhookSubscriptionDto()).ToList());
}
[Authorize(WebhooksManagementPermissions.WebhookSubscription.Update)]
public async virtual Task<WebhookSubscriptionDto> UpdateAsync(Guid id, WebhookSubscriptionUpdateInput input)
{
var subscription = await SubscriptionRepository.GetAsync(id);
if (!string.Equals(subscription.WebhookUri, input.WebhookUri))
{
await CheckSubscribedAsync(input);
}
subscription.SetSecret(input.Secret);
subscription.SetWebhookUri(input.WebhookUri);
subscription.SetWebhooks(input.ToSubscribedWebhooksString());
subscription.SetHeaders(input.ToWebhookHeadersString());
subscription.IsActive = input.IsActive;
await SubscriptionRepository.UpdateAsync(subscription);
await CurrentUnitOfWork.SaveChangesAsync();
return subscription.ToWebhookSubscriptionDto();
}
public async virtual Task<ListResultDto<WebhookAvailableGroupDto>> GetAllAvailableWebhooksAsync()
{
var groups = WebhookDefinitionManager.GetGroups();
var definitions = new List<WebhookAvailableGroupDto>();
foreach (var groupDefinition in groups)
{
var group = new WebhookAvailableGroupDto
{
Name = groupDefinition.Name,
DisplayName = groupDefinition.DisplayName?.Localize(StringLocalizerFactory),
};
foreach (var webhookDefinition in groupDefinition.Webhooks.OrderBy(d => d.Name))
{
if (await WebhookDefinitionManager.IsAvailableAsync(CurrentTenant.Id, webhookDefinition.Name))
{
group.Webhooks.Add(new WebhookAvailableDto
{
Name = webhookDefinition.Name,
Description = webhookDefinition.Description?.Localize(StringLocalizerFactory),
DisplayName = webhookDefinition.DisplayName?.Localize(StringLocalizerFactory)
});
}
}
definitions.Add(group);
}
return new ListResultDto<WebhookAvailableGroupDto>(definitions.OrderBy(d => d.Name).ToList());
}
protected async virtual Task CheckSubscribedAsync(WebhookSubscriptionCreateOrUpdateInput input)
{
foreach (var webhookName in input.Webhooks)
{
if (await SubscriptionRepository.IsSubscribedAsync(CurrentTenant.Id, input.WebhookUri, webhookName))
{
throw new BusinessException(WebhooksManagementErrorCodes.WebhookSubscription.DuplicateSubscribed)
.WithData(nameof(WebhookSubscription.WebhookUri), input.WebhookUri)
.WithData(nameof(WebhookSubscription.Webhooks), webhookName);
}
}
}
}

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

12
aspnet-core/modules/webhooks/LINGYUN.Abp.WebhooksManagement.Application/LINGYUN/Abp/WebhooksManagement/WebhooksManagementApplicationMapperProfile.cs

@ -0,0 +1,12 @@
using AutoMapper;
namespace LINGYUN.Abp.WebhooksManagement;
public class WebhooksManagementApplicationMapperProfile : Profile
{
public WebhooksManagementApplicationMapperProfile()
{
CreateMap<WebhookEventRecord, WebhookEventRecordDto>();
CreateMap<WebhookSendRecord, WebhookSendRecordDto>();
}
}

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>

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

Loading…
Cancel
Save