38 changed files with 538 additions and 95 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,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks> |
|||
<AssemblyName>LINGYUN.Abp.Notifications.Templating</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.Notifications.Templating</PackageId> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Notifications.Core\LINGYUN.Abp.Notifications.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,17 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public class AbpNotificationsResolveOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 模板解析提供者列表
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public List<INotificationTemplateResolveContributor> TemplateResolvers { get; } |
|||
|
|||
public AbpNotificationsResolveOptions() |
|||
{ |
|||
TemplateResolvers = new List<INotificationTemplateResolveContributor>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
|
|||
[DependsOn(typeof(AbpNotificationsCoreModule))] |
|||
public class AbpNotificationsTemplatingModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public interface INotificationTemplateResolveContext : IServiceProviderAccessor |
|||
{ |
|||
NotificationTemplate Template { get; } |
|||
|
|||
object Model { get; set; } |
|||
|
|||
bool Handled { get; set; } |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public interface INotificationTemplateResolveContributor |
|||
{ |
|||
string Name { get; } |
|||
|
|||
Task ResolveAsync(INotificationTemplateResolveContext context); |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
/// <summary>
|
|||
/// 通知模板模型解析接口
|
|||
/// </summary>
|
|||
public interface INotificationTemplateResolver |
|||
{ |
|||
/// <summary>
|
|||
/// 解析模板数据
|
|||
/// </summary>
|
|||
/// <param name="template"></param>
|
|||
/// <returns></returns>
|
|||
[NotNull] |
|||
Task<NotificationTemplateResolveResult> ResolveAsync(NotificationTemplate template); |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public class NotificationTemplateResolveContext : INotificationTemplateResolveContext |
|||
{ |
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
public NotificationTemplate Template { get; } |
|||
|
|||
public object Model { get; set; } |
|||
|
|||
public bool Handled { get; set; } |
|||
|
|||
public bool HasResolvedModel() |
|||
{ |
|||
return Handled || Model != null; |
|||
} |
|||
|
|||
public NotificationTemplateResolveContext( |
|||
NotificationTemplate template, |
|||
IServiceProvider serviceProvider) |
|||
{ |
|||
Template = template; |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public abstract class NotificationTemplateResolveContributorBase : INotificationTemplateResolveContributor |
|||
{ |
|||
public abstract string Name { get; } |
|||
/// <summary>
|
|||
/// 实现此接口处理模板数据
|
|||
/// </summary>
|
|||
/// <param name="context"></param>
|
|||
/// <returns></returns>
|
|||
public abstract Task ResolveAsync(INotificationTemplateResolveContext context); |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public class NotificationTemplateResolveResult |
|||
{ |
|||
/// <summary>
|
|||
/// 模板数据
|
|||
/// </summary>
|
|||
public object Model { get; set; } |
|||
|
|||
public List<string> AppliedResolvers { get; } |
|||
public NotificationTemplateResolveResult() |
|||
{ |
|||
AppliedResolvers = new List<string>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public class NotificationTemplateResolver : INotificationTemplateResolver, ITransientDependency |
|||
{ |
|||
private readonly IServiceProvider _serviceProvider; |
|||
private readonly AbpNotificationsResolveOptions _options; |
|||
|
|||
public NotificationTemplateResolver( |
|||
IOptions<AbpNotificationsResolveOptions> options, |
|||
IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
_options = options.Value; |
|||
} |
|||
public async virtual Task<NotificationTemplateResolveResult> ResolveAsync(NotificationTemplate template) |
|||
{ |
|||
var result = new NotificationTemplateResolveResult(); |
|||
|
|||
using (var serviceScope = _serviceProvider.CreateScope()) |
|||
{ |
|||
var context = new NotificationTemplateResolveContext(template, serviceScope.ServiceProvider); |
|||
|
|||
foreach (var resolveContributor in _options.TemplateResolvers) |
|||
{ |
|||
// TODO: 设定为每一个通知都配置自己的解析提供者?
|
|||
/** |
|||
if (resolveContributor.Name.Equals(template.Name)) |
|||
{ |
|||
await resolveContributor.ResolveAsync(context); |
|||
} |
|||
|
|||
if (context.HasResolvedModel()) |
|||
{ |
|||
result.Model = context.Model; |
|||
break; |
|||
} |
|||
**/ |
|||
|
|||
await resolveContributor.ResolveAsync(context); |
|||
|
|||
result.AppliedResolvers.Add(resolveContributor.Name); |
|||
|
|||
if (context.HasResolvedModel()) |
|||
{ |
|||
result.Model = context.Model; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
<Configurations>Debug;Release;</Configurations> |
|||
<Platforms>AnyCPU</Platforms> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> |
|||
<PackageReference Include="xunit" /> |
|||
<PackageReference Include="xunit.runner.visualstudio"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="Volo.Abp.Json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\realtime-notifications\LINGYUN.Abp.Notifications.Templating\LINGYUN.Abp.Notifications.Templating.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
|
|||
public class AbpNotificationsTemplatingTestBase : AbpTestsBase<AbpNotificationsTemplatingTestModule> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpNotificationsTemplatingModule), |
|||
typeof(AbpJsonModule), |
|||
typeof(AbpTestsBaseModule))] |
|||
public class AbpNotificationsTemplatingTestModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Newtonsoft.Json.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
internal class NewtownsoftJsonTemplateResolveContributor : INotificationTemplateResolveContributor |
|||
{ |
|||
public string Name => "ToDynamic"; |
|||
|
|||
public Task ResolveAsync(INotificationTemplateResolveContext context) |
|||
{ |
|||
var jsonObject = new JObject(); |
|||
foreach (var prop in context.Template.ExtraProperties) |
|||
{ |
|||
jsonObject.Add(prop.Key, prop.Value.ToString()); |
|||
} |
|||
context.Model = jsonObject.ToObject<NotificationSimpleModel>(); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
|
|||
public class NotificationSimpleModel |
|||
{ |
|||
public string Name { get; set; } |
|||
public string Firend { get; set; } |
|||
} |
|||
|
|||
public class NotificationModel |
|||
{ |
|||
public string Name { get; set; } |
|||
public List<NotificationJob> Jobs { get; set; } |
|||
} |
|||
|
|||
public class NotificationJob |
|||
{ |
|||
public string Name { get; set; } |
|||
public NotificationJob() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public NotificationJob(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
using LINGYUN.Abp.RealTime; |
|||
using Newtonsoft.Json; |
|||
using Shouldly; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Json; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
public class NotificationTemplateResolverTests : AbpNotificationsTemplatingTestBase |
|||
{ |
|||
private IJsonSerializer _jsonSerializer; |
|||
public NotificationTemplateResolverTests() |
|||
{ |
|||
_jsonSerializer = GetRequiredService<IJsonSerializer>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Resolve_Deserialize_To_Object() |
|||
{ |
|||
var notificationTemplate = new NotificationTemplate( |
|||
"Test", |
|||
data: new Dictionary<string, object> |
|||
{ |
|||
{ "Name", "Tom" }, |
|||
{ "Jobs", new List<NotificationJob> |
|||
{ |
|||
new NotificationJob("Catch Jerry"), |
|||
new NotificationJob("Hit Pike") |
|||
} |
|||
} |
|||
}); |
|||
|
|||
var receivedEto = _jsonSerializer.Deserialize<RealTimeEto<NotificationTemplate>>( |
|||
_jsonSerializer.Serialize( |
|||
new RealTimeEto<NotificationTemplate>(notificationTemplate))); |
|||
|
|||
var contributor = new ToObjectNotificationTemplateResolveContributor(); |
|||
var context = new NotificationTemplateResolveContext(receivedEto.Data, ServiceProvider); |
|||
|
|||
await contributor.ResolveAsync(context); |
|||
|
|||
var model = context.Model.ShouldBeOfType<NotificationModel>(); |
|||
model.Name.ShouldBe("Tom"); |
|||
model.Jobs.Count.ShouldBe(2); |
|||
model.Jobs[0].Name.ShouldBe("Catch Jerry"); |
|||
model.Jobs[1].Name.ShouldBe("Hit Pike"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Resolve_Deserialize_To_Dynamic() |
|||
{ |
|||
var notificationTemplate = new NotificationTemplate( |
|||
"Test", |
|||
data: new Dictionary<string, object> |
|||
{ |
|||
{ "Name", "Tom" }, |
|||
{ "Firend", "Jerry" } |
|||
}); |
|||
|
|||
var receivedEto = _jsonSerializer.Deserialize<RealTimeEto<NotificationTemplate>>( |
|||
_jsonSerializer.Serialize( |
|||
new RealTimeEto<NotificationTemplate>(notificationTemplate))); |
|||
|
|||
var contributor = new NewtownsoftJsonTemplateResolveContributor(); |
|||
var context = new NotificationTemplateResolveContext(receivedEto.Data, ServiceProvider); |
|||
|
|||
await contributor.ResolveAsync(context); |
|||
|
|||
dynamic model = context.Model; |
|||
|
|||
Assert.Equal(model.Name, "Tom"); |
|||
Assert.Equal(model.Firend, "Jerry"); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Templating; |
|||
internal class ToObjectNotificationTemplateResolveContributor : INotificationTemplateResolveContributor |
|||
{ |
|||
public string Name => "ToObject"; |
|||
|
|||
public Task ResolveAsync(INotificationTemplateResolveContext context) |
|||
{ |
|||
var model = new NotificationModel(); |
|||
|
|||
var nameObj = context.Template.GetProperty(nameof(NotificationModel.Name)); |
|||
model.Name = nameObj.ToString(); |
|||
|
|||
var jobsObj = context.Template.GetProperty(nameof(NotificationModel.Jobs)); |
|||
|
|||
var jsonSerializer = context.ServiceProvider.GetRequiredService<IJsonSerializer>(); |
|||
|
|||
model.Jobs = jsonSerializer.Deserialize<List<NotificationJob>>(jobsObj.ToString()); |
|||
|
|||
context.Model = model; |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue