120 changed files with 4184 additions and 0 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,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Features" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Http.Client" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,18 @@ |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.Features; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks; |
|||
|
|||
[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))] |
|||
[DependsOn(typeof(AbpFeaturesModule))] |
|||
[DependsOn(typeof(AbpGuidsModule))] |
|||
[DependsOn(typeof(AbpHttpClientModule))] |
|||
public class AbpWebhooksModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks; |
|||
|
|||
public class AbpWebhooksOptions |
|||
{ |
|||
public TimeSpan TimeoutDuration { get; set; } |
|||
|
|||
public int MaxSendAttemptCount { get; set; } |
|||
|
|||
public bool IsAutomaticSubscriptionDeactivationEnabled { get; set; } |
|||
|
|||
public int MaxConsecutiveFailCountBeforeDeactivateSubscription { get; set; } |
|||
|
|||
public ITypeList<WebhookDefinitionProvider> DefinitionProviders { get; } |
|||
|
|||
public AbpWebhooksOptions() |
|||
{ |
|||
TimeoutDuration = TimeSpan.FromSeconds(60); |
|||
MaxSendAttemptCount = 5; |
|||
MaxConsecutiveFailCountBeforeDeactivateSubscription = MaxSendAttemptCount * 3; |
|||
|
|||
DefinitionProviders = new TypeList<WebhookDefinitionProvider>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks.BackgroundWorker |
|||
{ |
|||
public class WebhookSenderJob : AsyncBackgroundJob<WebhookSenderArgs>, ITransientDependency |
|||
{ |
|||
private readonly IUnitOfWorkManager _unitOfWorkManager; |
|||
private readonly IWebhookSubscriptionManager _webhookSubscriptionManager; |
|||
private readonly IWebhookSendAttemptStore _webhookSendAttemptStore; |
|||
private readonly IWebhookSender _webhookSender; |
|||
|
|||
private readonly AbpWebhooksOptions _options; |
|||
|
|||
public WebhookSenderJob( |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IWebhookSubscriptionManager webhookSubscriptionManager, |
|||
IWebhookSendAttemptStore webhookSendAttemptStore, |
|||
IWebhookSender webhookSender, |
|||
IOptions<AbpWebhooksOptions> options) |
|||
{ |
|||
_unitOfWorkManager = unitOfWorkManager; |
|||
_webhookSubscriptionManager = webhookSubscriptionManager; |
|||
_webhookSendAttemptStore = webhookSendAttemptStore; |
|||
_webhookSender = webhookSender; |
|||
_options = options.Value; |
|||
} |
|||
|
|||
public override async Task ExecuteAsync(WebhookSenderArgs args) |
|||
{ |
|||
if (args.TryOnce) |
|||
{ |
|||
try |
|||
{ |
|||
await SendWebhook(args); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.LogWarning("An error occured while sending webhook with try once.", e); |
|||
// ignored
|
|||
} |
|||
} |
|||
else |
|||
{ |
|||
await SendWebhook(args); |
|||
} |
|||
} |
|||
|
|||
private async Task SendWebhook(WebhookSenderArgs args) |
|||
{ |
|||
if (args.WebhookEventId == default) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (args.WebhookSubscriptionId == default) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (!args.TryOnce) |
|||
{ |
|||
var sendAttemptCount = await _webhookSendAttemptStore.GetSendAttemptCountAsync( |
|||
args.TenantId, |
|||
args.WebhookEventId, |
|||
args.WebhookSubscriptionId |
|||
); |
|||
|
|||
if (sendAttemptCount > _options.MaxSendAttemptCount) |
|||
{ |
|||
return; |
|||
} |
|||
} |
|||
|
|||
try |
|||
{ |
|||
await _webhookSender.SendWebhookAsync(args); |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
// no need to retry to send webhook since subscription disabled
|
|||
if (!await TryDeactivateSubscriptionIfReachedMaxConsecutiveFailCount( |
|||
args.TenantId, |
|||
args.WebhookSubscriptionId)) |
|||
{ |
|||
throw; //Throw exception to re-try sending webhook
|
|||
} |
|||
} |
|||
} |
|||
|
|||
private async Task<bool> TryDeactivateSubscriptionIfReachedMaxConsecutiveFailCount( |
|||
Guid? tenantId, |
|||
Guid subscriptionId) |
|||
{ |
|||
if (!_options.IsAutomaticSubscriptionDeactivationEnabled) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var hasXConsecutiveFail = await _webhookSendAttemptStore |
|||
.HasXConsecutiveFailAsync( |
|||
tenantId, |
|||
subscriptionId, |
|||
_options.MaxConsecutiveFailCountBeforeDeactivateSubscription |
|||
); |
|||
|
|||
if (!hasXConsecutiveFail) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
using (var uow = _unitOfWorkManager.Begin()) |
|||
{ |
|||
await _webhookSubscriptionManager.ActivateWebhookSubscriptionAsync(subscriptionId, false); |
|||
await uow.CompleteAsync(); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public class DefaultWebhookPublisher : IWebhookPublisher |
|||
{ |
|||
public IWebhookEventStore WebhookEventStore { get; set; } |
|||
|
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IGuidGenerator _guidGenerator; |
|||
private readonly IJsonSerializer _jsonSerializer; |
|||
private readonly IBackgroundJobManager _backgroundJobManager; |
|||
private readonly IWebhookSubscriptionManager _webhookSubscriptionManager; |
|||
|
|||
public DefaultWebhookPublisher( |
|||
IWebhookSubscriptionManager webhookSubscriptionManager, |
|||
ICurrentTenant currentTenant, |
|||
IGuidGenerator guidGenerator, |
|||
IJsonSerializer jsonSerializer, |
|||
IBackgroundJobManager backgroundJobManager) |
|||
{ |
|||
_currentTenant = currentTenant; |
|||
_guidGenerator = guidGenerator; |
|||
_jsonSerializer = jsonSerializer; |
|||
_backgroundJobManager = backgroundJobManager; |
|||
_webhookSubscriptionManager = webhookSubscriptionManager; |
|||
|
|||
WebhookEventStore = NullWebhookEventStore.Instance; |
|||
} |
|||
|
|||
#region Async Publish Methods
|
|||
public virtual async Task PublishAsync(string webhookName, object data, bool sendExactSameData = false, WebhookHeader headers = null) |
|||
{ |
|||
var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsIfFeaturesGrantedAsync(_currentTenant.Id, webhookName); |
|||
await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers); |
|||
} |
|||
|
|||
public virtual async Task PublishAsync(string webhookName, object data, Guid? tenantId, |
|||
bool sendExactSameData = false, WebhookHeader headers = null) |
|||
{ |
|||
var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsIfFeaturesGrantedAsync(tenantId, webhookName); |
|||
await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers); |
|||
} |
|||
|
|||
public virtual async Task PublishAsync(Guid?[] tenantIds, string webhookName, object data, |
|||
bool sendExactSameData = false, WebhookHeader headers = null) |
|||
{ |
|||
var subscriptions = await _webhookSubscriptionManager.GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(tenantIds, webhookName); |
|||
await PublishAsync(webhookName, data, subscriptions, sendExactSameData, headers); |
|||
} |
|||
|
|||
private async Task PublishAsync(string webhookName, object data, List<WebhookSubscriptionInfo> webhookSubscriptions, |
|||
bool sendExactSameData = false, WebhookHeader headers = null) |
|||
{ |
|||
if (webhookSubscriptions.IsNullOrEmpty()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var subscriptionsGroupedByTenant = webhookSubscriptions.GroupBy(x => x.TenantId); |
|||
|
|||
foreach (var subscriptionGroupedByTenant in subscriptionsGroupedByTenant) |
|||
{ |
|||
var webhookInfo = await SaveAndGetWebhookAsync(subscriptionGroupedByTenant.Key, webhookName, data); |
|||
|
|||
foreach (var webhookSubscription in subscriptionGroupedByTenant) |
|||
{ |
|||
var headersToSend = webhookSubscription.Headers; |
|||
if (headers != null) |
|||
{ |
|||
if (headers.UseOnlyGivenHeaders)//do not use the headers defined in subscription
|
|||
{ |
|||
headersToSend = headers.Headers; |
|||
} |
|||
else |
|||
{ |
|||
//use the headers defined in subscription. If additional headers has same header, use additional headers value.
|
|||
foreach (var additionalHeader in headers.Headers) |
|||
{ |
|||
headersToSend[additionalHeader.Key] = additionalHeader.Value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
await _backgroundJobManager.EnqueueAsync(new WebhookSenderArgs |
|||
{ |
|||
TenantId = webhookSubscription.TenantId, |
|||
WebhookEventId = webhookInfo.Id, |
|||
Data = webhookInfo.Data, |
|||
WebhookName = webhookInfo.WebhookName, |
|||
WebhookSubscriptionId = webhookSubscription.Id, |
|||
Headers = headersToSend, |
|||
Secret = webhookSubscription.Secret, |
|||
WebhookUri = webhookSubscription.WebhookUri, |
|||
SendExactSameData = sendExactSameData |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
protected virtual async Task<WebhookEvent> SaveAndGetWebhookAsync(Guid? tenantId, string webhookName, |
|||
object data) |
|||
{ |
|||
var webhookInfo = new WebhookEvent |
|||
{ |
|||
Id = _guidGenerator.Create(), |
|||
WebhookName = webhookName, |
|||
Data = _jsonSerializer.Serialize(data), |
|||
TenantId = tenantId |
|||
}; |
|||
|
|||
await WebhookEventStore.InsertAndGetIdAsync(webhookInfo); |
|||
return webhookInfo; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Net; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public class DefaultWebhookSender : IWebhookSender |
|||
{ |
|||
public ILogger<DefaultWebhookSender> Logger { protected get; set; } |
|||
|
|||
private readonly AbpWebhooksOptions _options; |
|||
private readonly IWebhookManager _webhookManager; |
|||
|
|||
private const string FailedRequestDefaultContent = "Webhook Send Request Failed"; |
|||
|
|||
public DefaultWebhookSender( |
|||
IOptions<AbpWebhooksOptions> options, |
|||
IWebhookManager webhookManager) |
|||
{ |
|||
_options = options.Value; |
|||
_webhookManager = webhookManager; |
|||
|
|||
Logger = NullLogger<DefaultWebhookSender>.Instance; |
|||
} |
|||
|
|||
public async Task<Guid> SendWebhookAsync(WebhookSenderArgs webhookSenderArgs) |
|||
{ |
|||
if (webhookSenderArgs.WebhookEventId == default) |
|||
{ |
|||
throw new ArgumentNullException(nameof(webhookSenderArgs.WebhookEventId)); |
|||
} |
|||
|
|||
if (webhookSenderArgs.WebhookSubscriptionId == default) |
|||
{ |
|||
throw new ArgumentNullException(nameof(webhookSenderArgs.WebhookSubscriptionId)); |
|||
} |
|||
|
|||
var webhookSendAttemptId = await _webhookManager.InsertAndGetIdWebhookSendAttemptAsync(webhookSenderArgs); |
|||
|
|||
var request = CreateWebhookRequestMessage(webhookSenderArgs); |
|||
|
|||
var serializedBody = await _webhookManager.GetSerializedBodyAsync(webhookSenderArgs); |
|||
|
|||
_webhookManager.SignWebhookRequest(request, serializedBody, webhookSenderArgs.Secret); |
|||
|
|||
AddAdditionalHeaders(request, webhookSenderArgs); |
|||
|
|||
var isSucceed = false; |
|||
HttpStatusCode? statusCode = null; |
|||
var content = FailedRequestDefaultContent; |
|||
|
|||
try |
|||
{ |
|||
var response = await SendHttpRequest(request); |
|||
isSucceed = response.isSucceed; |
|||
statusCode = response.statusCode; |
|||
content = response.content; |
|||
} |
|||
catch (TaskCanceledException) |
|||
{ |
|||
statusCode = HttpStatusCode.RequestTimeout; |
|||
content = "Request Timeout"; |
|||
} |
|||
catch (HttpRequestException e) |
|||
{ |
|||
content = e.Message; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.LogError("An error occured while sending a webhook request", e); |
|||
} |
|||
finally |
|||
{ |
|||
await _webhookManager.StoreResponseOnWebhookSendAttemptAsync(webhookSendAttemptId, webhookSenderArgs.TenantId, statusCode, content); |
|||
} |
|||
|
|||
if (!isSucceed) |
|||
{ |
|||
throw new Exception($"Webhook sending attempt failed. WebhookSendAttempt id: {webhookSendAttemptId}"); |
|||
} |
|||
|
|||
return webhookSendAttemptId; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// You can override this to change request message
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
protected virtual HttpRequestMessage CreateWebhookRequestMessage(WebhookSenderArgs webhookSenderArgs) |
|||
{ |
|||
return new HttpRequestMessage(HttpMethod.Post, webhookSenderArgs.WebhookUri); |
|||
} |
|||
|
|||
protected virtual void AddAdditionalHeaders(HttpRequestMessage request, WebhookSenderArgs webhookSenderArgs) |
|||
{ |
|||
foreach (var header in webhookSenderArgs.Headers) |
|||
{ |
|||
if (request.Headers.TryAddWithoutValidation(header.Key, header.Value)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
throw new Exception($"Invalid Header. SubscriptionId:{webhookSenderArgs.WebhookSubscriptionId},Header: {header.Key}:{header.Value}"); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<(bool isSucceed, HttpStatusCode statusCode, string content)> SendHttpRequest(HttpRequestMessage request) |
|||
{ |
|||
using (var client = new HttpClient |
|||
{ |
|||
Timeout = _options.TimeoutDuration |
|||
}) |
|||
{ |
|||
var response = await client.SendAsync(request); |
|||
|
|||
var isSucceed = response.IsSuccessStatusCode; |
|||
var statusCode = response.StatusCode; |
|||
var content = await response.Content.ReadAsStringAsync(); |
|||
|
|||
return (isSucceed, statusCode, content); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public interface IWebhookDefinitionContext |
|||
{ |
|||
/// <summary>
|
|||
/// Adds the specified webhook definition. Throws exception if it is already added
|
|||
/// </summary>
|
|||
void Add(params WebhookDefinition[] definitions); |
|||
|
|||
/// <summary>
|
|||
/// Gets a webhook definition by name.
|
|||
/// Returns null if there is no webhook definition with given name.
|
|||
/// </summary>
|
|||
WebhookDefinition GetOrNull(string name); |
|||
|
|||
/// <summary>
|
|||
/// Remove webhook with given name
|
|||
/// </summary>
|
|||
/// <param name="name">webhook definition name</param>
|
|||
void Remove(string name); |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public interface IWebhookDefinitionManager |
|||
{ |
|||
/// <summary>
|
|||
/// Gets a webhook definition by name.
|
|||
/// Returns null if there is no webhook definition with given name.
|
|||
/// </summary>
|
|||
WebhookDefinition GetOrNull(string name); |
|||
|
|||
/// <summary>
|
|||
/// Gets a webhook definition by name.
|
|||
/// Throws exception if there is no webhook definition with given name.
|
|||
/// </summary>
|
|||
WebhookDefinition Get(string name); |
|||
|
|||
/// <summary>
|
|||
/// Gets all webhook definitions.
|
|||
/// </summary>
|
|||
IReadOnlyList<WebhookDefinition> GetAll(); |
|||
|
|||
/// <summary>
|
|||
/// Checks if given webhook name is available for given tenant.
|
|||
/// </summary>
|
|||
Task<bool> IsAvailableAsync(Guid? tenantId, string name); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public class NullWebhookSendAttemptStore : IWebhookSendAttemptStore |
|||
{ |
|||
public static NullWebhookSendAttemptStore Instance = new NullWebhookSendAttemptStore(); |
|||
|
|||
public Task InsertAsync(WebhookSendAttempt webhookSendAttempt) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public Task UpdateAsync(WebhookSendAttempt webhookSendAttempt) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public Task<WebhookSendAttempt> GetAsync(Guid? tenantId, Guid id) |
|||
{ |
|||
return Task.FromResult<WebhookSendAttempt>(default); |
|||
} |
|||
|
|||
public Task<int> GetSendAttemptCountAsync(Guid? tenantId, Guid webhookId, Guid webhookSubscriptionId) |
|||
{ |
|||
return Task.FromResult(int.MaxValue); |
|||
} |
|||
|
|||
public Task<bool> HasXConsecutiveFailAsync(Guid? tenantId, Guid subscriptionId, int searchCount) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
public Task<IReadOnlyCollection<WebhookSendAttempt>> GetAllSendAttemptsBySubscriptionAsPagedListAsync(Guid? tenantId, Guid subscriptionId, int maxResultCount, |
|||
int skipCount) |
|||
{ |
|||
return Task.FromResult(new List<WebhookSendAttempt>() as IReadOnlyCollection<WebhookSendAttempt>); |
|||
} |
|||
|
|||
public Task<List<WebhookSendAttempt>> GetAllSendAttemptsByWebhookEventIdAsync(Guid? tenantId, Guid webhookEventId) |
|||
{ |
|||
return Task.FromResult(new List<WebhookSendAttempt>()); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public class WebhookDefinition |
|||
{ |
|||
/// <summary>
|
|||
/// Unique name of the webhook.
|
|||
/// </summary>
|
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// Display name of the webhook.
|
|||
/// Optional.
|
|||
/// </summary>
|
|||
public ILocalizableString DisplayName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Description for the webhook.
|
|||
/// Optional.
|
|||
/// </summary>
|
|||
public ILocalizableString Description { get; set; } |
|||
|
|||
public List<string> RequiredFeatures { get; set; } |
|||
|
|||
public WebhookDefinition(string name, ILocalizableString displayName = null, ILocalizableString description = null) |
|||
{ |
|||
if (name.IsNullOrWhiteSpace()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(name), $"{nameof(name)} can not be null, empty or whitespace!"); |
|||
} |
|||
|
|||
Name = name.Trim(); |
|||
DisplayName = displayName; |
|||
Description = description; |
|||
|
|||
RequiredFeatures = new List<string>(); |
|||
} |
|||
|
|||
public WebhookDefinition WithFeature(params string[] features) |
|||
{ |
|||
if (!features.IsNullOrEmpty()) |
|||
{ |
|||
RequiredFeatures.AddRange(features); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return $"[{nameof(WebhookDefinition)} {Name}]"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public class WebhookDefinitionContext : IWebhookDefinitionContext |
|||
{ |
|||
protected Dictionary<string, WebhookDefinition> Webhooks { get; } |
|||
|
|||
public WebhookDefinitionContext(Dictionary<string, WebhookDefinition> webhooks) |
|||
{ |
|||
Webhooks = webhooks; |
|||
} |
|||
|
|||
public void Add(params WebhookDefinition[] definitions) |
|||
{ |
|||
if (definitions.IsNullOrEmpty()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var definition in definitions) |
|||
{ |
|||
Webhooks[definition.Name] = definition; |
|||
} |
|||
} |
|||
|
|||
public WebhookDefinition GetOrNull([NotNull] string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
if (!Webhooks.ContainsKey(name)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return Webhooks[name]; |
|||
} |
|||
|
|||
public void Remove(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
if (!Webhooks.ContainsKey(name)) |
|||
{ |
|||
throw new AbpException($"Undefined notification webhook: '{name}'."); |
|||
} |
|||
|
|||
Webhooks.Remove(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,109 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Features; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonDependency |
|||
{ |
|||
protected IDictionary<string, WebhookDefinition> WebhookDefinitions => _lazyWebhookDefinitions.Value; |
|||
private readonly Lazy<Dictionary<string, WebhookDefinition>> _lazyWebhookDefinitions; |
|||
|
|||
private readonly IServiceProvider _serviceProvider; |
|||
private readonly AbpWebhooksOptions _options; |
|||
|
|||
public WebhookDefinitionManager( |
|||
IServiceProvider serviceProvider, |
|||
IOptions<AbpWebhooksOptions> options) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
_options = options.Value; |
|||
|
|||
_lazyWebhookDefinitions = new Lazy<Dictionary<string, WebhookDefinition>>(CreateWebhookDefinitions); |
|||
} |
|||
|
|||
public WebhookDefinition GetOrNull(string name) |
|||
{ |
|||
if (!WebhookDefinitions.ContainsKey(name)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return WebhookDefinitions[name]; |
|||
} |
|||
|
|||
public WebhookDefinition Get(string name) |
|||
{ |
|||
if (!WebhookDefinitions.ContainsKey(name)) |
|||
{ |
|||
throw new KeyNotFoundException($"Webhook definitions does not contain a definition with the key \"{name}\"."); |
|||
} |
|||
|
|||
return WebhookDefinitions[name]; |
|||
} |
|||
|
|||
public IReadOnlyList<WebhookDefinition> GetAll() |
|||
{ |
|||
return WebhookDefinitions.Values.ToImmutableList(); |
|||
} |
|||
|
|||
public async Task<bool> IsAvailableAsync(Guid? tenantId, string name) |
|||
{ |
|||
if (tenantId == null) // host allowed to subscribe all webhooks
|
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
var webhookDefinition = GetOrNull(name); |
|||
|
|||
if (webhookDefinition == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (webhookDefinition.RequiredFeatures?.Any() == false) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
var currentTenant = _serviceProvider.GetRequiredService<ICurrentTenant>(); |
|||
var featureChecker = _serviceProvider.GetRequiredService<IFeatureChecker>(); |
|||
using (currentTenant.Change(tenantId)) |
|||
{ |
|||
if (!await featureChecker.IsEnabledAsync(true, webhookDefinition.RequiredFeatures.ToArray())) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
protected virtual Dictionary<string, WebhookDefinition> CreateWebhookDefinitions() |
|||
{ |
|||
var definitions = new Dictionary<string, WebhookDefinition>(); |
|||
|
|||
using (var scope = _serviceProvider.CreateScope()) |
|||
{ |
|||
var providers = _options |
|||
.DefinitionProviders |
|||
.Select(p => scope.ServiceProvider.GetRequiredService(p) as WebhookDefinitionProvider) |
|||
.ToList(); |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider.Define(new WebhookDefinitionContext(definitions)); |
|||
} |
|||
} |
|||
|
|||
return definitions; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Net; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public abstract class WebhookManager : IWebhookManager |
|||
{ |
|||
private const string SignatureHeaderKey = "sha256"; |
|||
private const string SignatureHeaderValueTemplate = SignatureHeaderKey + "={0}"; |
|||
private const string SignatureHeaderName = "abp-webhook-signature"; |
|||
|
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IWebhookSendAttemptStore WebhookSendAttemptStore { get; } |
|||
|
|||
protected WebhookManager( |
|||
IJsonSerializer jsonSerializer, |
|||
IWebhookSendAttemptStore webhookSendAttemptStore) |
|||
{ |
|||
JsonSerializer = jsonSerializer; |
|||
WebhookSendAttemptStore = webhookSendAttemptStore; |
|||
} |
|||
|
|||
public virtual async Task<WebhookPayload> GetWebhookPayloadAsync(WebhookSenderArgs webhookSenderArgs) |
|||
{ |
|||
var data = JsonSerializer.Serialize(webhookSenderArgs.Data); |
|||
|
|||
var attemptNumber = await WebhookSendAttemptStore.GetSendAttemptCountAsync( |
|||
webhookSenderArgs.TenantId, |
|||
webhookSenderArgs.WebhookEventId, |
|||
webhookSenderArgs.WebhookSubscriptionId); |
|||
|
|||
return new WebhookPayload( |
|||
webhookSenderArgs.WebhookEventId.ToString(), |
|||
webhookSenderArgs.WebhookName, |
|||
attemptNumber) |
|||
{ |
|||
Data = data |
|||
}; |
|||
} |
|||
|
|||
public virtual void SignWebhookRequest(HttpRequestMessage request, string serializedBody, string secret) |
|||
{ |
|||
if (request == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(request)); |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(serializedBody)) |
|||
{ |
|||
throw new ArgumentNullException(nameof(serializedBody)); |
|||
} |
|||
|
|||
request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json"); |
|||
|
|||
var secretBytes = Encoding.UTF8.GetBytes(secret); |
|||
var headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, serializedBody.Sha256(secretBytes)); |
|||
|
|||
request.Headers.Add(SignatureHeaderName, headerValue); |
|||
} |
|||
|
|||
public virtual async Task<string> GetSerializedBodyAsync(WebhookSenderArgs webhookSenderArgs) |
|||
{ |
|||
if (webhookSenderArgs.SendExactSameData) |
|||
{ |
|||
return webhookSenderArgs.Data; |
|||
} |
|||
|
|||
var payload = await GetWebhookPayloadAsync(webhookSenderArgs); |
|||
|
|||
var serializedBody = JsonSerializer.Serialize(payload); |
|||
|
|||
return serializedBody; |
|||
} |
|||
|
|||
public abstract Task<Guid> InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs); |
|||
|
|||
public abstract Task StoreResponseOnWebhookSendAttemptAsync(Guid webhookSendAttemptId, Guid? tenantId, HttpStatusCode? statusCode, string content); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public class WebhookSenderArgs |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
//Webhook information
|
|||
|
|||
/// <summary>
|
|||
/// <see cref="WebhookEvent"/> foreign id
|
|||
/// </summary>
|
|||
public Guid WebhookEventId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Webhook unique name
|
|||
/// </summary>
|
|||
public string WebhookName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Webhook data as JSON string.
|
|||
/// </summary>
|
|||
public string Data { get; set; } |
|||
|
|||
//Subscription information
|
|||
|
|||
/// <summary>
|
|||
/// <see cref="WebhookSubscription"/> foreign id
|
|||
/// </summary>
|
|||
public Guid WebhookSubscriptionId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Subscription webhook endpoint
|
|||
/// </summary>
|
|||
public string WebhookUri { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Webhook secret
|
|||
/// </summary>
|
|||
public string Secret { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a set of additional HTTP headers.That headers will be sent with the webhook.
|
|||
/// </summary>
|
|||
public IDictionary<string, string> Headers { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Tries to send webhook only one time without checking to send attempt count
|
|||
/// </summary>
|
|||
public bool TryOnce { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// True: It sends the exact same data as the parameter to clients.
|
|||
/// <para>
|
|||
/// False: It sends data in <see cref="WebhookPayload"/>. It is recommended way.
|
|||
/// </para>
|
|||
/// </summary>
|
|||
public bool SendExactSameData { get; set; } |
|||
|
|||
public WebhookSenderArgs() |
|||
{ |
|||
Headers = new Dictionary<string, string>(); |
|||
} |
|||
} |
|||
} |
|||
@ -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>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,175 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.Webhooks |
|||
{ |
|||
public class WebhookSubscriptionManager : IWebhookSubscriptionManager |
|||
{ |
|||
public IWebhookSubscriptionsStore WebhookSubscriptionsStore { get; set; } |
|||
|
|||
private readonly IGuidGenerator _guidGenerator; |
|||
private readonly IUnitOfWorkManager _unitOfWorkManager; |
|||
private readonly IWebhookDefinitionManager _webhookDefinitionManager; |
|||
|
|||
private const string WebhookSubscriptionSecretPrefix = "whs_"; |
|||
|
|||
public WebhookSubscriptionManager( |
|||
IGuidGenerator guidGenerator, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IWebhookDefinitionManager webhookDefinitionManager) |
|||
{ |
|||
_guidGenerator = guidGenerator; |
|||
_unitOfWorkManager = unitOfWorkManager; |
|||
_webhookDefinitionManager = webhookDefinitionManager; |
|||
|
|||
WebhookSubscriptionsStore = NullWebhookSubscriptionsStore.Instance; |
|||
} |
|||
|
|||
public virtual async Task<WebhookSubscriptionInfo> GetAsync(Guid id) |
|||
{ |
|||
return await WebhookSubscriptionsStore.GetAsync(id); |
|||
} |
|||
|
|||
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId) |
|||
{ |
|||
return await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId); |
|||
} |
|||
|
|||
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsIfFeaturesGrantedAsync(Guid? tenantId, string webhookName) |
|||
{ |
|||
if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) |
|||
{ |
|||
return new List<WebhookSubscriptionInfo>(); |
|||
} |
|||
|
|||
return (await WebhookSubscriptionsStore.GetAllSubscriptionsAsync(tenantId, webhookName)).ToList(); |
|||
} |
|||
|
|||
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds) |
|||
{ |
|||
return (await WebhookSubscriptionsStore.GetAllSubscriptionsOfTenantsAsync(tenantIds)).ToList(); |
|||
} |
|||
|
|||
public virtual async Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsIfFeaturesGrantedAsync(Guid?[] tenantIds, string webhookName) |
|||
{ |
|||
var featureGrantedTenants = new List<Guid?>(); |
|||
foreach (var tenantId in tenantIds) |
|||
{ |
|||
if (await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) |
|||
{ |
|||
featureGrantedTenants.Add(tenantId); |
|||
} |
|||
} |
|||
|
|||
return (await WebhookSubscriptionsStore.GetAllSubscriptionsOfTenantsAsync(featureGrantedTenants.ToArray(), webhookName)).ToList(); |
|||
} |
|||
|
|||
public virtual async Task<bool> IsSubscribedAsync(Guid? tenantId, string webhookName) |
|||
{ |
|||
if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return await WebhookSubscriptionsStore.IsSubscribedAsync(tenantId, webhookName); |
|||
} |
|||
|
|||
public virtual async Task AddOrUpdateSubscriptionAsync(WebhookSubscriptionInfo webhookSubscription) |
|||
{ |
|||
using (var uow = _unitOfWorkManager.Begin()) |
|||
{ |
|||
await CheckIfPermissionsGrantedAsync(webhookSubscription); |
|||
|
|||
if (webhookSubscription.Id == default) |
|||
{ |
|||
webhookSubscription.Id = _guidGenerator.Create(); |
|||
webhookSubscription.Secret = WebhookSubscriptionSecretPrefix + Guid.NewGuid().ToString("N"); |
|||
await WebhookSubscriptionsStore.InsertAsync(webhookSubscription); |
|||
} |
|||
else |
|||
{ |
|||
var subscription = await WebhookSubscriptionsStore.GetAsync(webhookSubscription.Id); |
|||
subscription.WebhookUri = webhookSubscription.WebhookUri; |
|||
subscription.Webhooks = webhookSubscription.Webhooks; |
|||
subscription.Headers = webhookSubscription.Headers; |
|||
await WebhookSubscriptionsStore.UpdateAsync(subscription); |
|||
} |
|||
|
|||
await uow.SaveChangesAsync(); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task ActivateWebhookSubscriptionAsync(Guid id, bool active) |
|||
{ |
|||
using (var uow = _unitOfWorkManager.Begin()) |
|||
{ |
|||
var webhookSubscription = await WebhookSubscriptionsStore.GetAsync(id); |
|||
webhookSubscription.IsActive = active; |
|||
|
|||
await uow.SaveChangesAsync(); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task DeleteSubscriptionAsync(Guid id) |
|||
{ |
|||
using (var uow = _unitOfWorkManager.Begin()) |
|||
{ |
|||
await WebhookSubscriptionsStore.DeleteAsync(id); |
|||
|
|||
await uow.SaveChangesAsync(); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task AddWebhookAsync(WebhookSubscriptionInfo subscription, string webhookName) |
|||
{ |
|||
using (var uow = _unitOfWorkManager.Begin()) |
|||
{ |
|||
await CheckPermissionsAsync(subscription.TenantId, webhookName); |
|||
webhookName = webhookName.Trim(); |
|||
if (webhookName.IsNullOrWhiteSpace()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(webhookName), $"{nameof(webhookName)} can not be null, empty or whitespace!"); |
|||
} |
|||
|
|||
if (!subscription.Webhooks.Contains(webhookName)) |
|||
{ |
|||
subscription.Webhooks.Add(webhookName); |
|||
|
|||
await WebhookSubscriptionsStore.UpdateAsync(subscription); |
|||
} |
|||
|
|||
await uow.SaveChangesAsync(); |
|||
} |
|||
} |
|||
|
|||
#region PermissionCheck
|
|||
|
|||
protected virtual async Task CheckIfPermissionsGrantedAsync(WebhookSubscriptionInfo webhookSubscription) |
|||
{ |
|||
if (webhookSubscription.Webhooks.IsNullOrEmpty()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var webhookDefinition in webhookSubscription.Webhooks) |
|||
{ |
|||
await CheckPermissionsAsync(webhookSubscription.TenantId, webhookDefinition); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task CheckPermissionsAsync(Guid? tenantId, string webhookName) |
|||
{ |
|||
if (!await _webhookDefinitionManager.IsAvailableAsync(tenantId, webhookName)) |
|||
{ |
|||
throw new AbpAuthorizationException($"Tenant \"{tenantId}\" must have necessary feature(s) to use webhook \"{webhookName}\""); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -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)); |
|||
} |
|||
} |
|||
@ -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,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> |
|||
@ -0,0 +1,22 @@ |
|||
using LINGYUN.Abp.WebhooksManagement.Localization; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.Authorization; |
|||
|
|||
public class WebhooksManagementPermissionDefinitionProvider : PermissionDefinitionProvider |
|||
{ |
|||
public override void Define(IPermissionDefinitionContext context) |
|||
{ |
|||
var group = context.AddGroup(WebhooksManagementPermissions.GroupName, L("Permission:WebhooksManagement")); |
|||
|
|||
group.AddPermission( |
|||
WebhooksManagementPermissions.ManageSettings, |
|||
L("Permission:ManageSettings")); |
|||
} |
|||
|
|||
private static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<WebhooksManagementResource>(name); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement.Authorization; |
|||
|
|||
public static class WebhooksManagementPermissions |
|||
{ |
|||
public const string GroupName = "WebhooksManagement"; |
|||
|
|||
public const string ManageSettings = GroupName + ".ManageSettings"; |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement.Features; |
|||
|
|||
public static class WebhooksManagementFeatureNames |
|||
{ |
|||
public const string GroupName = "WebhooksManagement"; |
|||
} |
|||
@ -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 |
|||
{ |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public static class WebhooksManagementRemoteServiceConsts |
|||
{ |
|||
public const string RemoteServiceName = "WebhooksManagement"; |
|||
public const string ModuleName = "WebhooksManagement"; |
|||
} |
|||
@ -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,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> |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using AutoMapper; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhooksManagementApplicationMapperProfile : Profile |
|||
{ |
|||
public WebhooksManagementApplicationMapperProfile() |
|||
{ |
|||
} |
|||
} |
|||
@ -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); |
|||
}); |
|||
} |
|||
} |
|||
@ -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,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\dapr\LINGYUN.Abp.Dapr.Client\LINGYUN.Abp.Dapr.Client.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Application.Contracts\LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,18 @@ |
|||
using LINGYUN.Abp.Dapr.Client; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpDaprClientModule), |
|||
typeof(WebhooksManagementApplicationContractsModule))] |
|||
public class WebhooksManagementDaprClientModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddDaprClientProxies( |
|||
typeof(WebhooksManagementApplicationContractsModule).Assembly, |
|||
WebhooksManagementRemoteServiceConsts.RemoteServiceName); |
|||
} |
|||
} |
|||
@ -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,25 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\WebhooksManagement\Localization\Resources\*.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\WebhooksManagement\Localization\Resources\*.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Auditing" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.EventBus" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Features:WebhooksManagement": "WebhooksManagement", |
|||
"Permission:WebhooksManagement": "WebhooksManagement", |
|||
"Permission:ManageSettings": "Manage Settings" |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Features:WebhooksManagement": "WebhooksManagement", |
|||
"Permission:WebhooksManagement": "WebhooksManagement", |
|||
"Permission:ManageSettings": "管理设置" |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.Localization; |
|||
|
|||
[LocalizationResourceName("WebhooksManagement")] |
|||
public class WebhooksManagementResource |
|||
{ |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using Volo.Abp.ObjectExtending.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending; |
|||
|
|||
public class WebhooksManagementModuleExtensionConfiguration : ModuleExtensionConfiguration |
|||
{ |
|||
public WebhooksManagementModuleExtensionConfiguration ConfigureWebhooksManagement( |
|||
Action<EntityExtensionConfiguration> configureAction) |
|||
{ |
|||
return this.ConfigureEntity( |
|||
WebhooksManagementModuleExtensionConsts.EntityNames.Entity, |
|||
configureAction |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using Volo.Abp.ObjectExtending.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending; |
|||
|
|||
public static class WebhooksManagementModuleExtensionConfigurationDictionaryExtensions |
|||
{ |
|||
public static ModuleExtensionConfigurationDictionary ConfigureWebhooksManagement( |
|||
this ModuleExtensionConfigurationDictionary modules, |
|||
Action<WebhooksManagementModuleExtensionConfiguration> configureAction) |
|||
{ |
|||
return modules.ConfigureModule( |
|||
WebhooksManagementModuleExtensionConsts.ModuleName, |
|||
configureAction |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement.ObjectExtending; |
|||
|
|||
public static class WebhooksManagementModuleExtensionConsts |
|||
{ |
|||
public const string ModuleName = "WebhooksManagement"; |
|||
|
|||
public static class EntityNames |
|||
{ |
|||
public const string Entity = "Entity"; |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using LINGYUN.Abp.WebhooksManagement.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Localization.ExceptionHandling; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpLocalizationModule))] |
|||
public class WebhooksManagementDomainSharedModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<WebhooksManagementDomainSharedModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<WebhooksManagementResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/WebhooksManagement/Localization/Resources"); |
|||
}); |
|||
|
|||
Configure<AbpExceptionLocalizationOptions>(options => |
|||
{ |
|||
options.MapCodeNamespace(WebhooksManagementErrorCodes.Namespace, typeof(WebhooksManagementResource)); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public static class WebhooksManagementErrorCodes |
|||
{ |
|||
public const string Namespace = "WebhooksManagement"; |
|||
} |
|||
@ -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,22 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Caching" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Domain.Shared\LINGYUN.Abp.WebhooksManagement.Domain.Shared.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.WebHooks\LINGYUN.Abp.WebHooks.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,70 @@ |
|||
using LINGYUN.Abp.Webhooks; |
|||
using System; |
|||
using System.Net; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class DefaultWebhookManager : WebhookManager, ITransientDependency |
|||
{ |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IWebhookSendRecordRepository WebhookSendAttemptRepository { get; } |
|||
public DefaultWebhookManager( |
|||
ICurrentTenant currentTenant, |
|||
IGuidGenerator guidGenerator, |
|||
IJsonSerializer jsonSerializer, |
|||
IWebhookSendAttemptStore webhookSendAttemptStore, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IWebhookSendRecordRepository webhookSendAttemptRepository) |
|||
: base(jsonSerializer, webhookSendAttemptStore) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
GuidGenerator = guidGenerator; |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
WebhookSendAttemptRepository = webhookSendAttemptRepository; |
|||
} |
|||
|
|||
public async override Task<Guid> InsertAndGetIdWebhookSendAttemptAsync(WebhookSenderArgs webhookSenderArgs) |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(webhookSenderArgs.TenantId)) |
|||
{ |
|||
var record = new WebhookSendRecord( |
|||
GuidGenerator.Create(), |
|||
webhookSenderArgs.WebhookEventId, |
|||
webhookSenderArgs.WebhookSubscriptionId, |
|||
webhookSenderArgs.TenantId); |
|||
|
|||
await WebhookSendAttemptRepository.InsertAsync(record); |
|||
|
|||
await uow.SaveChangesAsync(); |
|||
|
|||
return record.Id; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public async override Task StoreResponseOnWebhookSendAttemptAsync(Guid webhookSendAttemptId, Guid? tenantId, HttpStatusCode? statusCode, string content) |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var record = await WebhookSendAttemptRepository.GetAsync(webhookSendAttemptId); |
|||
record.SetResponse(content, statusCode); |
|||
|
|||
await WebhookSendAttemptRepository.UpdateAsync(record); |
|||
|
|||
await uow.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public interface IWebhookEventRecordRepository : IRepository<WebhookEventRecord, Guid> |
|||
{ |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public interface IWebhookSendRecordRepository : IRepository<WebhookSendRecord, Guid> |
|||
{ |
|||
Task<int> GetCountAsync( |
|||
WebhookSendRecordFilter filter, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<WebhookSendRecord>> GetListAsync( |
|||
WebhookSendRecordFilter filter, |
|||
string sorting = nameof(WebhookSendRecord.CreationTime), |
|||
int maxResultCount = 10, |
|||
int skipCount = 10, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public interface IWebhookSubscriptionRepository : IRepository<WebhookSubscription, Guid> |
|||
{ |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.Settings |
|||
{ |
|||
public class WebhooksManagementSettingDefinitionProvider : SettingDefinitionProvider |
|||
{ |
|||
public override void Define(ISettingDefinitionContext context) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement.Settings |
|||
{ |
|||
public static class WebhooksManagementSettings |
|||
{ |
|||
public const string GroupName = "WebhooksManagement"; |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhookEventRecord : Entity<Guid>, IMultiTenant, IHasCreationTime, IHasDeletionTime |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual string WebhookName { get; protected set; } |
|||
public virtual string Data { get; protected set; } |
|||
public virtual DateTime CreationTime { get; set; } |
|||
public virtual DateTime? DeletionTime { get; set; } |
|||
public virtual bool IsDeleted { get; set; } |
|||
protected WebhookEventRecord() |
|||
{ |
|||
} |
|||
|
|||
public WebhookEventRecord( |
|||
Guid id, |
|||
string webhookName, |
|||
string data, |
|||
Guid? tenantId = null) : base(id) |
|||
{ |
|||
WebhookName = webhookName; |
|||
Data = data; |
|||
TenantId = tenantId; |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
using LINGYUN.Abp.Webhooks; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.ObjectMapping; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhookEventStore : DomainService, IWebhookEventStore |
|||
{ |
|||
protected IObjectMapper<WebhooksManagementDomainModule> ObjectMapper => LazyServiceProvider.LazyGetRequiredService<IObjectMapper<WebhooksManagementDomainModule>>(); |
|||
|
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IWebhookEventRecordRepository WebhookEventRepository { get; } |
|||
|
|||
public WebhookEventStore( |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IWebhookEventRecordRepository webhookEventRepository) |
|||
{ |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
WebhookEventRepository = webhookEventRepository; |
|||
} |
|||
|
|||
public async virtual Task<WebhookEvent> GetAsync(Guid? tenantId, Guid id) |
|||
{ |
|||
using var uow = UnitOfWorkManager.Begin(); |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var record = await WebhookEventRepository.GetAsync(id); |
|||
|
|||
return ObjectMapper.Map<WebhookEventRecord, WebhookEvent>(record); |
|||
} |
|||
} |
|||
|
|||
public async virtual Task<Guid> InsertAndGetIdAsync(WebhookEvent webhookEvent) |
|||
{ |
|||
using var uow = UnitOfWorkManager.Begin(); |
|||
using (CurrentTenant.Change(webhookEvent.TenantId)) |
|||
{ |
|||
var record = new WebhookEventRecord( |
|||
GuidGenerator.Create(), |
|||
webhookEvent.WebhookName, |
|||
webhookEvent.Data, |
|||
webhookEvent.TenantId) |
|||
{ |
|||
CreationTime = Clock.Now, |
|||
}; |
|||
|
|||
await WebhookEventRepository.InsertAsync(record); |
|||
|
|||
await uow.SaveChangesAsync(); |
|||
|
|||
return record.Id; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,165 @@ |
|||
using LINGYUN.Abp.Webhooks; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Net; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.Linq; |
|||
using Volo.Abp.ObjectMapping; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhookSendAttemptStore : DomainService, IWebhookSendAttemptStore |
|||
{ |
|||
protected IObjectMapper<WebhooksManagementDomainModule> ObjectMapper => LazyServiceProvider.LazyGetRequiredService<IObjectMapper<WebhooksManagementDomainModule>>(); |
|||
protected IAsyncQueryableExecuter AsyncQueryableExecuter => LazyServiceProvider.LazyGetRequiredService<IAsyncQueryableExecuter>(); |
|||
|
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IWebhookSendRecordRepository WebhookSendAttemptRepository { get; } |
|||
|
|||
public WebhookSendAttemptStore( |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IWebhookSendRecordRepository webhookSendAttemptRepository) |
|||
{ |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
WebhookSendAttemptRepository = webhookSendAttemptRepository; |
|||
} |
|||
|
|||
public async virtual Task<(int TotalCount, IReadOnlyCollection<WebhookSendAttempt> Webhooks)> GetAllSendAttemptsBySubscriptionAsPagedListAsync( |
|||
Guid? tenantId, |
|||
Guid subscriptionId, |
|||
int maxResultCount, |
|||
int skipCount) |
|||
{ |
|||
(int TotalCount, IReadOnlyCollection<WebhookSendAttempt> Webhooks) sendAttempts; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var filter = new WebhookSendRecordFilter |
|||
{ |
|||
SubscriptionId = subscriptionId, |
|||
}; |
|||
var totalCount = await WebhookSendAttemptRepository.GetCountAsync(filter); |
|||
|
|||
var list = await WebhookSendAttemptRepository.GetListAsync( |
|||
filter, |
|||
maxResultCount: maxResultCount, |
|||
skipCount: skipCount); |
|||
|
|||
var webHooks = ObjectMapper.Map<List<WebhookSendRecord>, List<WebhookSendAttempt>>(list); |
|||
|
|||
sendAttempts = ValueTuple.Create(totalCount, webHooks.ToImmutableList()); |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
return sendAttempts; |
|||
} |
|||
|
|||
public async virtual Task<List<WebhookSendAttempt>> GetAllSendAttemptsByWebhookEventIdAsync( |
|||
Guid? tenantId, |
|||
Guid webhookEventId) |
|||
{ |
|||
List<WebhookSendAttempt> sendAttempts; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var queryable = await WebhookSendAttemptRepository.GetQueryableAsync(); |
|||
|
|||
var list = await AsyncQueryableExecuter.ToListAsync(queryable |
|||
.Where(attempt => attempt.WebhookEventId == webhookEventId) |
|||
.OrderByDescending(attempt => attempt.CreationTime) |
|||
); |
|||
|
|||
sendAttempts = ObjectMapper.Map<List<WebhookSendRecord>, List<WebhookSendAttempt>>(list); |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
return sendAttempts; |
|||
} |
|||
|
|||
public async virtual Task<WebhookSendAttempt> GetAsync( |
|||
Guid? tenantId, |
|||
Guid id) |
|||
{ |
|||
WebhookSendRecord sendAttempt; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
sendAttempt = await WebhookSendAttemptRepository.GetAsync(id); |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
return ObjectMapper.Map<WebhookSendRecord, WebhookSendAttempt>(sendAttempt); |
|||
} |
|||
|
|||
public async virtual Task<int> GetSendAttemptCountAsync( |
|||
Guid? tenantId, |
|||
Guid webhookEventId, |
|||
Guid webhookSubscriptionId) |
|||
{ |
|||
int sendAttemptCount; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
sendAttemptCount = await WebhookSendAttemptRepository.CountAsync(attempt => |
|||
attempt.WebhookEventId == webhookEventId && |
|||
attempt.WebhookSubscriptionId == webhookSubscriptionId); |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
return sendAttemptCount; |
|||
} |
|||
|
|||
public async virtual Task<bool> HasXConsecutiveFailAsync( |
|||
Guid? tenantId, |
|||
Guid subscriptionId, |
|||
int failCount) |
|||
{ |
|||
bool result; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
if (await WebhookSendAttemptRepository.CountAsync(x => x.WebhookSubscriptionId == subscriptionId) < failCount) |
|||
{ |
|||
result = false; |
|||
} |
|||
else |
|||
{ |
|||
var queryable = await WebhookSendAttemptRepository.GetQueryableAsync(); |
|||
|
|||
result = !await AsyncQueryableExecuter.AnyAsync(queryable |
|||
.OrderByDescending(attempt => attempt.CreationTime) |
|||
.Take(failCount) |
|||
.Where(attempt => attempt.ResponseStatusCode == HttpStatusCode.OK) |
|||
); |
|||
} |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
using System; |
|||
using System.Net; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhookSendRecord : Entity<Guid>, IHasCreationTime, IHasModificationTime, IMultiTenant |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
|
|||
public virtual Guid WebhookEventId { get; protected set; } |
|||
|
|||
public virtual Guid WebhookSubscriptionId { get; protected set; } |
|||
|
|||
public virtual string Response { get; protected set; } |
|||
|
|||
public virtual HttpStatusCode? ResponseStatusCode { get; set; } |
|||
|
|||
public virtual DateTime CreationTime { get; set; } |
|||
|
|||
public virtual DateTime? LastModificationTime { get; set; } |
|||
|
|||
public virtual WebhookEventRecord WebhookEvent { get; protected set; } |
|||
|
|||
protected WebhookSendRecord() |
|||
{ |
|||
} |
|||
|
|||
public WebhookSendRecord( |
|||
Guid id, |
|||
Guid eventId, |
|||
Guid subscriptionId, |
|||
Guid? tenantId = null) : base(id) |
|||
{ |
|||
WebhookEventId = eventId; |
|||
WebhookSubscriptionId = subscriptionId; |
|||
|
|||
TenantId = tenantId; |
|||
} |
|||
|
|||
public void SetResponse( |
|||
string response, |
|||
HttpStatusCode? statusCode = null) |
|||
{ |
|||
Response = response; |
|||
ResponseStatusCode = statusCode; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Net; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhookSendRecordFilter |
|||
{ |
|||
public string Filter { get; set; } |
|||
|
|||
public Guid? WebhookEventId { get; set; } |
|||
|
|||
public Guid? SubscriptionId { get; set; } |
|||
|
|||
public string Response { get; set; } |
|||
|
|||
public HttpStatusCode? ResponseStatusCode { get; set; } |
|||
|
|||
public DateTime? BeginCreationTime { get; set; } |
|||
|
|||
public DateTime? EndCreationTime { get; set; } |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhookSubscription : CreationAuditedEntity<Guid> |
|||
{ |
|||
public virtual Guid? TenantId { get; protected set; } |
|||
public virtual string WebhookUri { get; protected set; } |
|||
public virtual string Secret { get; protected set; } |
|||
public virtual bool IsActive { get; set; } |
|||
public virtual string Webhooks { get; protected set; } |
|||
public virtual string Headers { get; protected set; } |
|||
protected WebhookSubscription() |
|||
{ |
|||
} |
|||
public WebhookSubscription( |
|||
Guid id, |
|||
string webhookUri, |
|||
string secret, |
|||
string webhooks, |
|||
string headers, |
|||
Guid? tenantId = null) : base(id) |
|||
{ |
|||
WebhookUri = webhookUri; |
|||
Secret = secret; |
|||
Webhooks = webhooks; |
|||
Headers = headers; |
|||
TenantId = tenantId; |
|||
|
|||
IsActive = true; |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using LINGYUN.Abp.Webhooks; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Services; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhookSubscriptionsStore : DomainService, IWebhookSubscriptionsStore |
|||
{ |
|||
public Task DeleteAsync(Guid id) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsAsync(Guid? tenantId, string webhookName) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task<List<WebhookSubscriptionInfo>> GetAllSubscriptionsOfTenantsAsync(Guid?[] tenantIds, string webhookName) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task<WebhookSubscriptionInfo> GetAsync(Guid id) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task InsertAsync(WebhookSubscriptionInfo webhookSubscription) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task<bool> IsSubscribedAsync(Guid? tenantId, string webhookName) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task UpdateAsync(WebhookSubscriptionInfo webhookSubscription) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public static class WebhooksManagementDbProperties |
|||
{ |
|||
public static string DbTablePrefix { get; set; } = "WebhooksManagement_"; |
|||
|
|||
public static string DbSchema { get; set; } = null; |
|||
|
|||
|
|||
public const string ConnectionStringName = "WebhooksManagement"; |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using AutoMapper; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
public class WebhooksManagementDomainMapperProfile : Profile |
|||
{ |
|||
public WebhooksManagementDomainMapperProfile() |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using LINGYUN.Abp.Webhooks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAutoMapperModule), |
|||
typeof(AbpWebhooksModule), |
|||
typeof(WebhooksManagementDomainSharedModule))] |
|||
public class WebhooksManagementDomainModule : AbpModule |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new(); |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAutoMapperObjectMapper<WebhooksManagementDomainModule>(); |
|||
|
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<WebhooksManagementDomainMapperProfile>(validate: true); |
|||
}); |
|||
|
|||
Configure<AbpDistributedEntityEventOptions>(options => |
|||
{ |
|||
}); |
|||
} |
|||
|
|||
public override void PostConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
// 扩展实体配置
|
|||
//ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity(
|
|||
// WebhooksManagementModuleExtensionConsts.ModuleName,
|
|||
// WebhooksManagementModuleExtensionConsts.EntityNames.Entity,
|
|||
// typeof(Entity)
|
|||
//);
|
|||
}); |
|||
} |
|||
} |
|||
@ -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>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Domain\LINGYUN.Abp.WebhooksManagement.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; |
|||
|
|||
[ConnectionStringName(WebhooksManagementDbProperties.ConnectionStringName)] |
|||
public interface IWebhooksManagementDbContext : IEfCoreDbContext |
|||
{ |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; |
|||
|
|||
[ConnectionStringName(WebhooksManagementDbProperties.ConnectionStringName)] |
|||
public class WebhooksManagementDbContext : AbpDbContext<WebhooksManagementDbContext>, IWebhooksManagementDbContext |
|||
{ |
|||
public WebhooksManagementDbContext( |
|||
DbContextOptions<WebhooksManagementDbContext> options) : base(options) |
|||
{ |
|||
} |
|||
|
|||
protected override void OnModelCreating(ModelBuilder modelBuilder) |
|||
{ |
|||
base.OnModelCreating(modelBuilder); |
|||
|
|||
modelBuilder.ConfigureWebhooksManagement(); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; |
|||
|
|||
public static class WebhooksManagementDbContextModelCreatingExtensions |
|||
{ |
|||
public static void ConfigureWebhooksManagement( |
|||
this ModelBuilder builder, |
|||
Action<WebhooksManagementModelBuilderConfigurationOptions> optionsAction = null) |
|||
{ |
|||
Check.NotNull(builder, nameof(builder)); |
|||
|
|||
var options = new WebhooksManagementModelBuilderConfigurationOptions( |
|||
WebhooksManagementDbProperties.DbTablePrefix, |
|||
WebhooksManagementDbProperties.DbSchema |
|||
); |
|||
optionsAction?.Invoke(options); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; |
|||
|
|||
public static class WebhooksManagementEfCoreQueryableExtensions |
|||
{ |
|||
// 在此聚合仓储服务的扩展方法
|
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; |
|||
|
|||
[DependsOn( |
|||
typeof(WebhooksManagementDomainModule), |
|||
typeof(AbpEntityFrameworkCoreModule))] |
|||
public class WebhooksManagementEntityFrameworkCoreModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAbpDbContext<WebhooksManagementDbContext>(options => |
|||
{ |
|||
options.AddDefaultRepositories<IWebhooksManagementDbContext>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement.EntityFrameworkCore; |
|||
|
|||
public class WebhooksManagementModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions |
|||
{ |
|||
public WebhooksManagementModelBuilderConfigurationOptions( |
|||
[NotNull] string tablePrefix = "", |
|||
[CanBeNull] string schema = null) |
|||
: base( |
|||
tablePrefix, |
|||
schema) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -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.Http.Client" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Application.Contracts\LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,18 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.WebhooksManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), |
|||
typeof(WebhooksManagementApplicationContractsModule))] |
|||
public class WebhooksManagementHttpApiClientModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(WebhooksManagementApplicationContractsModule).Assembly, |
|||
WebhooksManagementRemoteServiceConsts.RemoteServiceName); |
|||
} |
|||
} |
|||
@ -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>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.WebhooksManagement.Application.Contracts\LINGYUN.Abp.WebhooksManagement.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue