35 changed files with 555 additions and 41 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -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> |
||||
@ -0,0 +1,15 @@ |
|||||
|
<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.Features" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,34 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Features; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Webhooks; |
||||
|
|
||||
|
[DependsOn(typeof(AbpFeaturesModule))] |
||||
|
public class AbpWebhooksCoreModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
AutoAddDefinitionProviders(context.Services); |
||||
|
} |
||||
|
|
||||
|
private static void AutoAddDefinitionProviders(IServiceCollection services) |
||||
|
{ |
||||
|
var definitionProviders = new List<Type>(); |
||||
|
|
||||
|
services.OnRegistred(context => |
||||
|
{ |
||||
|
if (typeof(IWebhookDefinitionProvider).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
definitionProviders.Add(context.ImplementationType); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
services.Configure<AbpWebhooksOptions>(options => |
||||
|
{ |
||||
|
options.DefinitionProviders.AddIfNotContains(definitionProviders); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.Webhooks |
||||
|
{ |
||||
|
public interface IWebhookDefinitionProvider |
||||
|
{ |
||||
|
void Define(IWebhookDefinitionContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -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> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Webhooks\LINGYUN.Abp.Webhooks.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,19 @@ |
|||||
|
using Volo.Abp.EventBus; |
||||
|
using Volo.Abp.Json.SystemTextJson; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Webhooks.EventBus; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpWebhooksModule), |
||||
|
typeof(AbpEventBusModule))] |
||||
|
public class AbpWebhooksEventBusModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpSystemTextJsonSerializerOptions>(options => |
||||
|
{ |
||||
|
options.UnsupportedTypes.TryAdd<WebhooksEventData>(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Webhooks.EventBus; |
||||
|
|
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
public class DistributedEventBusWebhookPublisher : IWebhookPublisher, ITransientDependency |
||||
|
{ |
||||
|
protected IDistributedEventBus EventBus { get; } |
||||
|
|
||||
|
public DistributedEventBusWebhookPublisher(IDistributedEventBus eventBus) |
||||
|
{ |
||||
|
EventBus = eventBus; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task PublishAsync( |
||||
|
string webhookName, |
||||
|
object data, |
||||
|
bool sendExactSameData = false, |
||||
|
WebhookHeader headers = null) |
||||
|
{ |
||||
|
var eventData = new WebhooksEventData( |
||||
|
webhookName, |
||||
|
data, |
||||
|
sendExactSameData, |
||||
|
headers); |
||||
|
|
||||
|
await PublishAsync(eventData); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task PublishAsync( |
||||
|
string webhookName, |
||||
|
object data, |
||||
|
Guid? tenantId, |
||||
|
bool sendExactSameData = false, |
||||
|
WebhookHeader headers = null) |
||||
|
{ |
||||
|
var eventData = new WebhooksEventData( |
||||
|
webhookName, |
||||
|
data, |
||||
|
sendExactSameData, |
||||
|
headers, |
||||
|
new Guid?[] { tenantId }); |
||||
|
|
||||
|
await PublishAsync(eventData); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task PublishAsync( |
||||
|
Guid?[] tenantIds, |
||||
|
string webhookName, |
||||
|
object data, |
||||
|
bool sendExactSameData = false, |
||||
|
WebhookHeader headers = null) |
||||
|
{ |
||||
|
var eventData = new WebhooksEventData( |
||||
|
webhookName, |
||||
|
data, |
||||
|
sendExactSameData, |
||||
|
headers, |
||||
|
tenantIds); |
||||
|
|
||||
|
await PublishAsync(eventData); |
||||
|
} |
||||
|
|
||||
|
protected async virtual Task PublishAsync(WebhooksEventData eventData) |
||||
|
{ |
||||
|
await EventBus.PublishAsync(eventData); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.EventBus; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Webhooks.EventBus; |
||||
|
|
||||
|
[Serializable] |
||||
|
[EventName("abp.webhooks.events")] |
||||
|
public class WebhooksEventData |
||||
|
{ |
||||
|
public Guid?[] TenantIds { get; set; } |
||||
|
|
||||
|
public string WebhookName { get; set; } |
||||
|
|
||||
|
public object Data { get; set; } |
||||
|
|
||||
|
public bool SendExactSameData { get; set; } |
||||
|
|
||||
|
public WebhookHeader Headers { get; set; } |
||||
|
|
||||
|
public WebhooksEventData() |
||||
|
{ |
||||
|
Headers = new WebhookHeader(); |
||||
|
TenantIds = new Guid?[0]; |
||||
|
} |
||||
|
|
||||
|
public WebhooksEventData( |
||||
|
string webhookName, |
||||
|
object data, |
||||
|
bool sendExactSameData = false, |
||||
|
WebhookHeader headers = null, |
||||
|
Guid?[] tenantIds = null) |
||||
|
{ |
||||
|
WebhookName = webhookName; |
||||
|
Data = data; |
||||
|
SendExactSameData = sendExactSameData; |
||||
|
Headers = headers ?? new WebhookHeader(); |
||||
|
TenantIds = tenantIds ?? new Guid?[0]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
using LINGYUN.Abp.Webhooks; |
||||
|
using LINGYUN.Abp.Webhooks.EventBus; |
||||
|
using Newtonsoft.Json; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundJobs; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LY.MicroService.WebhooksManagement.EventBus.Handlers; |
||||
|
|
||||
|
public class WebhooksEventHandler : |
||||
|
IDistributedEventHandler<WebhooksEventData>, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
public IWebhookEventStore WebhookEventStore { get; set; } |
||||
|
|
||||
|
private readonly ICurrentTenant _currentTenant; |
||||
|
private readonly IBackgroundJobManager _backgroundJobManager; |
||||
|
private readonly IWebhookSubscriptionManager _webhookSubscriptionManager; |
||||
|
|
||||
|
public WebhooksEventHandler( |
||||
|
IWebhookSubscriptionManager webhookSubscriptionManager, |
||||
|
ICurrentTenant currentTenant, |
||||
|
IBackgroundJobManager backgroundJobManager) |
||||
|
{ |
||||
|
_currentTenant = currentTenant; |
||||
|
_backgroundJobManager = backgroundJobManager; |
||||
|
_webhookSubscriptionManager = webhookSubscriptionManager; |
||||
|
|
||||
|
WebhookEventStore = NullWebhookEventStore.Instance; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task HandleEventAsync(WebhooksEventData eventData) |
||||
|
{ |
||||
|
var subscriptions = await _webhookSubscriptionManager |
||||
|
.GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync( |
||||
|
eventData.TenantIds, |
||||
|
eventData.WebhookName); |
||||
|
|
||||
|
await PublishAsync(eventData.WebhookName, eventData.Data, subscriptions, eventData.SendExactSameData, eventData.Headers); |
||||
|
} |
||||
|
|
||||
|
protected async virtual 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 |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected async virtual 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; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue