31 changed files with 750 additions and 29 deletions
@ -0,0 +1,33 @@ |
|||||
|
namespace LINGYUN.Abp.Notifications; |
||||
|
public static class NotificationDataExtensions |
||||
|
{ |
||||
|
private const string Prefix = "push-plus:"; |
||||
|
private const string WebhookKey = Prefix + "webhook"; |
||||
|
private const string CallbackUrlKey = Prefix + "callback"; |
||||
|
|
||||
|
public static void SetWebhook( |
||||
|
this NotificationData notificationData, |
||||
|
string url) |
||||
|
{ |
||||
|
notificationData.TrySetData(WebhookKey, url); |
||||
|
} |
||||
|
|
||||
|
public static string GetWebhookOrNull( |
||||
|
this NotificationData notificationData) |
||||
|
{ |
||||
|
return notificationData.TryGetData(WebhookKey)?.ToString(); |
||||
|
} |
||||
|
|
||||
|
public static void SetCallbackUrl( |
||||
|
this NotificationData notificationData, |
||||
|
string callbackUrl) |
||||
|
{ |
||||
|
notificationData.TrySetData(CallbackUrlKey, callbackUrl); |
||||
|
} |
||||
|
|
||||
|
public static string GetCallbackUrlOrNull( |
||||
|
this NotificationData notificationData) |
||||
|
{ |
||||
|
return notificationData.TryGetData(CallbackUrlKey)?.ToString(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait /> |
||||
|
</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 @@ |
|||||
|
namespace LINGYUN.Abp.Notifications; |
||||
|
public static class NotificationDataExtensions |
||||
|
{ |
||||
|
private const string Prefix = "wx-pusher:"; |
||||
|
private const string UrlKey = Prefix + "url"; |
||||
|
|
||||
|
public static void SetUrl( |
||||
|
this NotificationData notificationData, |
||||
|
string url) |
||||
|
{ |
||||
|
notificationData.TrySetData(UrlKey, url); |
||||
|
} |
||||
|
|
||||
|
public static string GetUrlOrNull( |
||||
|
this NotificationData notificationData) |
||||
|
{ |
||||
|
return notificationData.TryGetData(UrlKey)?.ToString(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,205 @@ |
|||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Json; |
||||
|
using Volo.Abp.TextTemplating; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications; |
||||
|
|
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
public class FakeNotificationSender : INotificationSender, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<FakeNotificationSender> Logger { get; set; } |
||||
|
|
||||
|
protected AbpNotificationOptions Options { get; } |
||||
|
|
||||
|
protected IJsonSerializer JsonSerializer { get; } |
||||
|
|
||||
|
protected ITemplateRenderer TemplateRenderer { get; } |
||||
|
|
||||
|
protected INotificationStore NotificationStore { get; } |
||||
|
|
||||
|
protected IStringLocalizerFactory StringLocalizerFactory { get; } |
||||
|
|
||||
|
protected INotificationDefinitionManager NotificationDefinitionManager { get; } |
||||
|
|
||||
|
protected INotificationSubscriptionManager NotificationSubscriptionManager { get; } |
||||
|
|
||||
|
protected INotificationPublishProviderManager NotificationPublishProviderManager { get; } |
||||
|
|
||||
|
public FakeNotificationSender( |
||||
|
IJsonSerializer jsonSerializer, |
||||
|
ITemplateRenderer templateRenderer, |
||||
|
IStringLocalizerFactory stringLocalizerFactory, |
||||
|
IOptions<AbpNotificationOptions> options, |
||||
|
INotificationStore notificationStore, |
||||
|
INotificationDefinitionManager notificationDefinitionManager, |
||||
|
INotificationSubscriptionManager notificationSubscriptionManager, |
||||
|
INotificationPublishProviderManager notificationPublishProviderManager) |
||||
|
{ |
||||
|
Options = options.Value; |
||||
|
JsonSerializer = jsonSerializer; |
||||
|
TemplateRenderer = templateRenderer; |
||||
|
StringLocalizerFactory = stringLocalizerFactory; |
||||
|
NotificationStore = notificationStore; |
||||
|
NotificationDefinitionManager = notificationDefinitionManager; |
||||
|
NotificationSubscriptionManager = notificationSubscriptionManager; |
||||
|
NotificationPublishProviderManager = notificationPublishProviderManager; |
||||
|
|
||||
|
Logger = NullLogger<FakeNotificationSender>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<string> SendNofiterAsync( |
||||
|
string name, |
||||
|
NotificationData data, |
||||
|
IEnumerable<UserIdentifier> users = null, |
||||
|
Guid? tenantId = null, |
||||
|
NotificationSeverity severity = NotificationSeverity.Info) |
||||
|
{ |
||||
|
var notification = NotificationDefinitionManager.GetOrNull(name); |
||||
|
if (notification == null) |
||||
|
{ |
||||
|
return ""; |
||||
|
} |
||||
|
|
||||
|
var notificationInfo = new NotificationInfo |
||||
|
{ |
||||
|
Name = notification.Name, |
||||
|
CreationTime = DateTime.Now, |
||||
|
Data = data, |
||||
|
Severity = severity, |
||||
|
Lifetime = notification.NotificationLifetime, |
||||
|
TenantId = tenantId, |
||||
|
Type = notification.NotificationType |
||||
|
}; |
||||
|
notificationInfo.SetId(DateTimeOffset.Now.ToUnixTimeMilliseconds()); |
||||
|
|
||||
|
notificationInfo.Data = NotificationDataConverter.Convert(notificationInfo.Data); |
||||
|
|
||||
|
Logger.LogDebug($"Persistent notification {notificationInfo.Name}"); |
||||
|
|
||||
|
await NotificationStore.InsertNotificationAsync(notificationInfo); |
||||
|
|
||||
|
var providers = Enumerable.Reverse(NotificationPublishProviderManager.Providers); |
||||
|
|
||||
|
if (notification.Providers.Any()) |
||||
|
{ |
||||
|
providers = providers.Where(p => notification.Providers.Contains(p.Name)); |
||||
|
} |
||||
|
|
||||
|
await PublishFromProvidersAsync( |
||||
|
providers, |
||||
|
users ?? new List<UserIdentifier>(), |
||||
|
notificationInfo); |
||||
|
|
||||
|
return notificationInfo.Id; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<string> SendNofiterAsync( |
||||
|
string name, |
||||
|
NotificationTemplate template, |
||||
|
IEnumerable<UserIdentifier> users = null, |
||||
|
Guid? tenantId = null, |
||||
|
NotificationSeverity severity = NotificationSeverity.Info) |
||||
|
{ |
||||
|
var notification = NotificationDefinitionManager.GetOrNull(name); |
||||
|
if (notification == null) |
||||
|
{ |
||||
|
return ""; |
||||
|
} |
||||
|
|
||||
|
var notificationInfo = new NotificationInfo |
||||
|
{ |
||||
|
Name = notification.Name, |
||||
|
TenantId = tenantId, |
||||
|
Severity = severity, |
||||
|
Type = notification.NotificationType, |
||||
|
CreationTime = DateTime.Now, |
||||
|
Lifetime = notification.NotificationLifetime, |
||||
|
}; |
||||
|
notificationInfo.SetId(DateTimeOffset.Now.ToUnixTimeMilliseconds()); |
||||
|
|
||||
|
var title = notification.DisplayName.Localize(StringLocalizerFactory); |
||||
|
|
||||
|
var message = await TemplateRenderer.RenderAsync( |
||||
|
templateName: name, |
||||
|
model: template.ExtraProperties); |
||||
|
|
||||
|
var notificationData = new NotificationData(); |
||||
|
notificationData.WriteStandardData( |
||||
|
title: title, |
||||
|
message: message, |
||||
|
createTime: notificationInfo.CreationTime, |
||||
|
formUser: "Fake User"); |
||||
|
notificationData.ExtraProperties.AddIfNotContains(template.ExtraProperties); |
||||
|
|
||||
|
notificationInfo.Data = notificationData; |
||||
|
|
||||
|
Logger.LogDebug($"Persistent notification {notificationInfo.Name}"); |
||||
|
|
||||
|
// 持久化通知
|
||||
|
await NotificationStore.InsertNotificationAsync(notificationInfo); |
||||
|
|
||||
|
var providers = Enumerable.Reverse(NotificationPublishProviderManager.Providers); |
||||
|
|
||||
|
// 过滤用户指定提供者
|
||||
|
if (notification.Providers.Any()) |
||||
|
{ |
||||
|
providers = providers.Where(p => notification.Providers.Contains(p.Name)); |
||||
|
} |
||||
|
|
||||
|
await PublishFromProvidersAsync( |
||||
|
providers, |
||||
|
users ?? new List<UserIdentifier>(), |
||||
|
notificationInfo); |
||||
|
|
||||
|
return notificationInfo.Id; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 指定提供者发布通知
|
||||
|
/// </summary>
|
||||
|
/// <param name="providers">提供者列表</param>
|
||||
|
/// <param name="notificationInfo">通知信息</param>
|
||||
|
/// <returns></returns>
|
||||
|
protected async Task PublishFromProvidersAsync( |
||||
|
IEnumerable<INotificationPublishProvider> providers, |
||||
|
IEnumerable<UserIdentifier> users, |
||||
|
NotificationInfo notificationInfo) |
||||
|
{ |
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
await PublishAsync(provider, notificationInfo, users); |
||||
|
} |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 发布通知
|
||||
|
/// </summary>
|
||||
|
/// <param name="provider">通知发布者</param>
|
||||
|
/// <param name="notificationInfo">通知信息</param>
|
||||
|
/// <param name="subscriptionUserIdentifiers">订阅用户列表</param>
|
||||
|
/// <returns></returns>
|
||||
|
protected async Task PublishAsync( |
||||
|
INotificationPublishProvider provider, |
||||
|
NotificationInfo notificationInfo, |
||||
|
IEnumerable<UserIdentifier> subscriptionUserIdentifiers) |
||||
|
{ |
||||
|
Logger.LogDebug($"Sending notification with provider {provider.Name}"); |
||||
|
var notifacationDataMapping = Options.NotificationDataMappings |
||||
|
.GetMapItemOrDefault(provider.Name, notificationInfo.Name); |
||||
|
if (notifacationDataMapping != null) |
||||
|
{ |
||||
|
notificationInfo.Data = notifacationDataMapping.MappingFunc(notificationInfo.Data); |
||||
|
} |
||||
|
// 发布
|
||||
|
await provider.PublishAsync(notificationInfo, subscriptionUserIdentifiers); |
||||
|
|
||||
|
Logger.LogDebug($"Send notification {notificationInfo.Name} with provider {provider.Name} was successful"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
<IsPackable>false</IsPackable> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\modules\wx-pusher\LINGYUN.Abp.Notifications.WxPusher\LINGYUN.Abp.Notifications.WxPusher.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.WxPusher.Tests\LINGYUN.Abp.WxPusher.Tests.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Notifications.Tests\LINGYUN.Abp.Notifications.Tests.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,7 @@ |
|||||
|
using LINGYUN.Abp.Tests; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.WxPusher; |
||||
|
|
||||
|
public abstract class AbpNotificationsWxPusherTestBase : AbpTestsBase<AbpNotificationsWxPusherTestModule> |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using LINGYUN.Abp.WxPusher; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.WxPusher; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpNotificationsWxPusherModule), |
||||
|
typeof(AbpWxPusherTestModule), |
||||
|
typeof(AbpNotificationsTestsModule))] |
||||
|
public class AbpNotificationsWxPusherTestModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.WxPusher; |
||||
|
|
||||
|
public class NotificationSenderTests : AbpNotificationsWxPusherTestBase |
||||
|
{ |
||||
|
protected INotificationSender NotificationSender { get; } |
||||
|
|
||||
|
public NotificationSenderTests() |
||||
|
{ |
||||
|
NotificationSender = GetRequiredService<INotificationSender>(); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData( |
||||
|
"Title from the Xunit unit test", |
||||
|
"Text content from the Xunit unit test. \r\n Click the link at the top to redirect baidu site.")] |
||||
|
public async Task Send_Text_Test( |
||||
|
string title, |
||||
|
string message) |
||||
|
{ |
||||
|
var notificationData = new NotificationData(); |
||||
|
notificationData.WriteStandardData( |
||||
|
title, |
||||
|
message, |
||||
|
DateTime.Now, |
||||
|
"xUnit Test"); |
||||
|
notificationData.SetUrl("https://www.baidu.com/"); |
||||
|
|
||||
|
await NotificationSender.SendNofiterAsync( |
||||
|
NotificationsTestsNames.Test1, |
||||
|
notificationData); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData( |
||||
|
"Title from the Xunit unit test", |
||||
|
"<h>Html content from the Xunit unit test.</h> <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>")] |
||||
|
public async Task Send_Html_Test( |
||||
|
string title, |
||||
|
string message) |
||||
|
{ |
||||
|
var notificationData = new NotificationData(); |
||||
|
notificationData.WriteStandardData( |
||||
|
title, |
||||
|
message, |
||||
|
DateTime.Now, |
||||
|
"xUnit Test"); |
||||
|
notificationData.SetUrl("https://www.baidu.com/"); |
||||
|
|
||||
|
await NotificationSender.SendNofiterAsync( |
||||
|
NotificationsTestsNames.Test2, |
||||
|
notificationData); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData( |
||||
|
"Title from the Xunit unit test", |
||||
|
"**Markdown content from the Xunit unit test.** <br /> <a href=\"https://www.baidu.com/\">Click to redirect baidu site.</a>")] |
||||
|
public async Task Send_Markdown_Test( |
||||
|
string title, |
||||
|
string message) |
||||
|
{ |
||||
|
var notificationData = new NotificationData(); |
||||
|
notificationData.WriteStandardData( |
||||
|
title, |
||||
|
message, |
||||
|
DateTime.Now, |
||||
|
"xUnit Test"); |
||||
|
notificationData.SetUrl("https://www.baidu.com/"); |
||||
|
|
||||
|
await NotificationSender.SendNofiterAsync( |
||||
|
NotificationsTestsNames.Test3, |
||||
|
notificationData); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using LINGYUN.Abp.WxPusher.Messages; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.WxPusher; |
||||
|
|
||||
|
public class NotificationsWxPusherTestsDefinitionProvider : NotificationDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(INotificationDefinitionContext context) |
||||
|
{ |
||||
|
var group = context.GetGroupOrNull(NotificationsTestsNames.GroupName); |
||||
|
|
||||
|
var nt1 = group.Notifications.FirstOrDefault(n => n.Name.Equals(NotificationsTestsNames.Test1)); |
||||
|
nt1.WithProviders(WxPusherNotificationPublishProvider.ProviderName) |
||||
|
.WithContentType(MessageContentType.Text) |
||||
|
.WithTopics(new List<int> { 7182 }); |
||||
|
|
||||
|
var nt2 = group.Notifications.FirstOrDefault(n => n.Name.Equals(NotificationsTestsNames.Test2)); |
||||
|
nt2.WithProviders(WxPusherNotificationPublishProvider.ProviderName) |
||||
|
.WithContentType(MessageContentType.Html) |
||||
|
.WithTopics(new List<int> { 7182 }); |
||||
|
|
||||
|
var nt3 = group.Notifications.FirstOrDefault(n => n.Name.Equals(NotificationsTestsNames.Test3)); |
||||
|
nt3.WithProviders(WxPusherNotificationPublishProvider.ProviderName) |
||||
|
.WithContentType(MessageContentType.Markdown) |
||||
|
.WithTopics(new List<int> { 7182 }); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
global using Xunit; |
||||
|
global using Shouldly; |
||||
Loading…
Reference in new issue