Skip to content

Commit f7fe64a

Browse files
authored
Merge branch 'main' into allow-put-without-payload
2 parents f1c4683 + 9cbd916 commit f7fe64a

File tree

7 files changed

+145
-38
lines changed

7 files changed

+145
-38
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Cnblogs.Architecture.Ddd.EventBus.Abstractions;
2+
3+
/// <summary>
4+
/// The empty interface as a generic type constraint
5+
/// </summary>
6+
public interface IEventBusHandler
7+
{
8+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
using MediatR;
1+
using MediatR;
22

33
namespace Cnblogs.Architecture.Ddd.EventBus.Abstractions;
44

55
/// <summary>
66
/// 集成事件处理器。
77
/// </summary>
88
/// <typeparam name="TEvent">集成事件。</typeparam>
9-
public interface IIntegrationEventHandler<TEvent> : INotificationHandler<TEvent>
9+
public interface IIntegrationEventHandler<TEvent> : INotificationHandler<TEvent>, IEventBusHandler
1010
where TEvent : IntegrationEvent
1111
{
12-
}
12+
}

src/Cnblogs.Architecture.Ddd.EventBus.Dapr/EndPointExtensions.cs

Lines changed: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using System.Reflection;
1+
using System.Reflection;
22
using Cnblogs.Architecture.Ddd.EventBus.Abstractions;
33
using Cnblogs.Architecture.Ddd.EventBus.Dapr;
4+
using MediatR;
45
using Microsoft.AspNetCore.Routing;
56
using Microsoft.Extensions.DependencyInjection;
67
using Microsoft.Extensions.Options;
@@ -19,20 +20,11 @@ public static class EndPointExtensions
1920
/// <param name="builder"><see cref="IEndpointRouteBuilder"/></param>
2021
/// <typeparam name="TEvent">事件类型。</typeparam>
2122
/// <returns><see cref="IEndpointConventionBuilder"/></returns>
22-
public static IEndpointConventionBuilder Subscribe<TEvent>(this IEndpointRouteBuilder builder)
23+
public static IEndpointRouteBuilder Subscribe<TEvent>(this IEndpointRouteBuilder builder)
2324
where TEvent : IntegrationEvent
2425
{
25-
var attr = typeof(TEvent).Assembly
26-
.GetCustomAttributes(typeof(AssemblyAppNameAttribute), false)
27-
.Cast<AssemblyAppNameAttribute>()
28-
.FirstOrDefault();
29-
if (attr is null || string.IsNullOrEmpty(attr.Name))
30-
{
31-
throw new InvalidOperationException(
32-
$"No AppName was configured in assembly for event: {typeof(TEvent).Name}, either use Subscribe<TEvent>(string appName) method to set AppName manually or add [assembly:AssemblyAppName()] to the Assembly that {typeof(TEvent).Name} belongs to");
33-
}
34-
35-
return builder.Subscribe<TEvent>(attr.Name);
26+
var appName = typeof(TEvent).Assembly.GetAppName();
27+
return builder.Subscribe<TEvent>(appName);
3628
}
3729

3830
/// <summary>
@@ -42,7 +34,7 @@ public static IEndpointConventionBuilder Subscribe<TEvent>(this IEndpointRouteBu
4234
/// <param name="appName">事件隶属名称。</param>
4335
/// <typeparam name="TEvent">事件类型。</typeparam>
4436
/// <returns></returns>
45-
public static IEndpointConventionBuilder Subscribe<TEvent>(this IEndpointRouteBuilder builder, string appName)
37+
public static IEndpointRouteBuilder Subscribe<TEvent>(this IEndpointRouteBuilder builder, string appName)
4638
where TEvent : IntegrationEvent
4739
{
4840
var eventName = typeof(TEvent).Name;
@@ -57,7 +49,7 @@ public static IEndpointConventionBuilder Subscribe<TEvent>(this IEndpointRouteBu
5749
/// <param name="appName">应用名称。</param>
5850
/// <typeparam name="TEvent">事件类型。</typeparam>
5951
/// <returns></returns>
60-
public static IEndpointConventionBuilder Subscribe<TEvent>(
52+
public static IEndpointRouteBuilder Subscribe<TEvent>(
6153
this IEndpointRouteBuilder builder,
6254
string route,
6355
string appName)
@@ -68,36 +60,78 @@ public static IEndpointConventionBuilder Subscribe<TEvent>(
6860
var result = builder
6961
.MapPost(route, (TEvent receivedEvent, IEventBus eventBus) => eventBus.ReceiveAsync(receivedEvent))
7062
.WithTopic(DaprOptions.PubSubName, DaprUtils.GetDaprTopicName<TEvent>(appName));
71-
return result;
63+
64+
return builder;
7265
}
7366

7467
/// <summary>
7568
/// 订阅 Assembly 中的全部事件。
7669
/// </summary>
7770
/// <param name="builder"><see cref="IEndpointRouteBuilder"/></param>
7871
/// <param name="assemblies"><see cref="Assembly"/></param>
79-
public static void Subscribe(this IEndpointRouteBuilder builder, params Assembly[] assemblies)
72+
public static IEndpointRouteBuilder Subscribe(this IEndpointRouteBuilder builder, params Assembly[] assemblies)
8073
{
8174
builder.EnsureDaprEventBus();
8275

83-
var method = typeof(EndPointExtensions).GetMethod(
84-
nameof(Subscribe),
85-
new[] { typeof(IEndpointRouteBuilder), typeof(string) })!;
76+
var method = GetSubscribeMethod();
77+
8678
foreach (var assembly in assemblies)
8779
{
8880
var events = assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(IntegrationEvent))).ToList();
89-
var attr = assembly
90-
.GetCustomAttributes(typeof(AssemblyAppNameAttribute), false)
91-
.Cast<AssemblyAppNameAttribute>()
92-
.FirstOrDefault();
93-
if (attr is null || string.IsNullOrEmpty(attr.Name))
81+
var appName = assembly.GetAppName();
82+
events.ForEach(e => method.InvokeSubscribe(e, builder, appName));
83+
}
84+
85+
return builder;
86+
}
87+
88+
/// <summary>
89+
/// Subscribes integration events that the TEventHandler implements
90+
/// </summary>
91+
/// <typeparam name="TEventHandler">The integration event handler that implements <![CDATA[IIntegrationEventHandler<TEvent>]]></typeparam>
92+
/// <param name="builder"><see cref="IEndpointRouteBuilder"/></param>
93+
public static IEndpointRouteBuilder SubscribeByEventHandler<TEventHandler>(this IEndpointRouteBuilder builder)
94+
where TEventHandler : IEventBusHandler
95+
{
96+
return builder.SubscribeByEventHandler(typeof(TEventHandler));
97+
}
98+
99+
/// <summary>
100+
/// Subscribes integration events that event handlers implement in assemblies
101+
/// </summary>
102+
/// <param name="builder"><see cref="IEndpointRouteBuilder"/></param>
103+
/// <param name="assemblies">assemblies that event handlers reside</param>
104+
/// <returns></returns>
105+
public static IEndpointRouteBuilder SubscribeByEventHandler(this IEndpointRouteBuilder builder, params Assembly[] assemblies)
106+
{
107+
foreach (var assembly in assemblies)
108+
{
109+
foreach (Type type in assembly.GetTypes())
94110
{
95-
throw new InvalidOperationException(
96-
$"No AppName was configured in assembly: {assembly.FullName}, either use Subscribe<TEvent>(string appName) method to set AppName manually or add [assembly:AssemblyAppName()] to the Assembly");
111+
builder.SubscribeByEventHandler(type);
97112
}
113+
}
114+
115+
return builder;
116+
}
98117

99-
events.ForEach(e => method.MakeGenericMethod(e).Invoke(null, new object[] { builder, attr.Name }));
118+
private static IEndpointRouteBuilder SubscribeByEventHandler(this IEndpointRouteBuilder builder, Type type)
119+
{
120+
var interfaces = type.GetInterfaces()
121+
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IIntegrationEventHandler<>));
122+
123+
foreach (var handlerInterface in interfaces)
124+
{
125+
var eventType = handlerInterface.GetGenericArguments().FirstOrDefault();
126+
if (eventType != null)
127+
{
128+
var assembly = eventType.Assembly;
129+
var appName = assembly.GetAppName();
130+
GetSubscribeMethod().InvokeSubscribe(eventType, builder, appName);
131+
}
100132
}
133+
134+
return builder;
101135
}
102136

103137
private static void EnsureEventBusRegistered(this IEndpointRouteBuilder builder, DaprOptions daprOptions)
@@ -142,4 +176,32 @@ private static void EnsureDaprEventBus(this IEndpointRouteBuilder builder)
142176
builder.EnsureDaprSubscribeHandlerMapped(options);
143177
builder.EnsureEventBusRegistered(options);
144178
}
145-
}
179+
180+
private static MethodInfo GetSubscribeMethod()
181+
{
182+
return typeof(EndPointExtensions).GetMethod(
183+
nameof(Subscribe),
184+
new[] { typeof(IEndpointRouteBuilder), typeof(string) })!;
185+
}
186+
187+
private static void InvokeSubscribe(this MethodInfo method, Type eventType, IEndpointRouteBuilder builder, string appName)
188+
{
189+
method.MakeGenericMethod(eventType).Invoke(null, new object[] { builder, appName });
190+
}
191+
192+
private static string GetAppName(this Assembly assembly)
193+
{
194+
var appName = assembly
195+
.GetCustomAttributes(typeof(AssemblyAppNameAttribute), false)
196+
.Cast<AssemblyAppNameAttribute>()
197+
.FirstOrDefault()?.Name;
198+
199+
if (string.IsNullOrEmpty(appName))
200+
{
201+
throw new InvalidOperationException(
202+
$"No AppName was configured in assembly: {assembly.FullName}, either use Subscribe<TEvent>(string appName) method to set AppName manually or add [assembly:AssemblyAppName()] to the Assembly");
203+
}
204+
205+
return appName;
206+
}
207+
}

test/Cnblogs.Architecture.IntegrationTestProject/EventHandlers/TestIntegrationEventHandler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
namespace Cnblogs.Architecture.IntegrationTestProject.EventHandlers;
66

7-
public class TestIntegrationEventHandler : IIntegrationEventHandler<TestIntegrationEvent>
7+
public class TestIntegrationEventHandler : IIntegrationEventHandler<TestIntegrationEvent>,
8+
IIntegrationEventHandler<BlogPostCreatedIntegrationEvent>
89
{
910
private readonly ILogger _logger;
1011

@@ -19,4 +20,11 @@ public Task Handle(TestIntegrationEvent notification, CancellationToken cancella
1920

2021
return Task.CompletedTask;
2122
}
23+
24+
public Task Handle(BlogPostCreatedIntegrationEvent notification, CancellationToken cancellationToken)
25+
{
26+
_logger.LogInformation(LogTemplates.HandledIntegratonEvent, notification);
27+
28+
return Task.CompletedTask;
29+
}
2230
}

test/Cnblogs.Architecture.IntegrationTests/DaprTests.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Diagnostics;
22
using System.Net;
3+
using Cnblogs.Architecture.IntegrationTestProject.EventHandlers;
34
using Cnblogs.Architecture.TestIntegrationEvents;
45
using FluentAssertions;
56
using Microsoft.AspNetCore.Builder;
@@ -10,16 +11,29 @@ namespace Cnblogs.Architecture.IntegrationTests;
1011

1112
public class DaprTests
1213
{
13-
[Fact]
14-
public async Task Dapr_SubscribeEndpoint_OkAsync()
14+
[Theory]
15+
[InlineData(SubscribeType.ByEvent)]
16+
[InlineData(SubscribeType.ByEventAssemblies)]
17+
[InlineData(SubscribeType.ByEventHandler)]
18+
[InlineData(SubscribeType.ByEventHandlerAssemblies)]
19+
public async Task Dapr_SubscribeEndpoint_OkAsync(SubscribeType subscribeType)
1520
{
1621
// Arrange
1722
var builder = WebApplication.CreateBuilder();
1823
builder.Services.AddDaprEventBus(nameof(DaprTests));
1924
builder.WebHost.UseTestServer();
2025

21-
var app = builder.Build();
22-
app.Subscribe<TestIntegrationEvent>();
26+
using var app = builder.Build();
27+
28+
_ = subscribeType switch
29+
{
30+
SubscribeType.ByEvent => app.Subscribe<TestIntegrationEvent>().Subscribe<BlogPostCreatedIntegrationEvent>(),
31+
SubscribeType.ByEventAssemblies => app.Subscribe(typeof(TestIntegrationEvent).Assembly),
32+
SubscribeType.ByEventHandler => app.SubscribeByEventHandler<TestIntegrationEventHandler>(),
33+
SubscribeType.ByEventHandlerAssemblies => app.SubscribeByEventHandler(typeof(TestIntegrationEventHandler).Assembly),
34+
_ => app
35+
};
36+
2337
await app.StartAsync();
2438
var httpClient = app.GetTestClient();
2539

@@ -29,8 +43,8 @@ public async Task Dapr_SubscribeEndpoint_OkAsync()
2943
// Assert
3044
response.Should().BeSuccessful();
3145
var responseText = await response.Content.ReadAsStringAsync();
32-
Debug.WriteLine(responseText);
3346
responseText.Should().Contain(nameof(TestIntegrationEvent));
47+
responseText.Should().Contain(nameof(BlogPostCreatedIntegrationEvent));
3448
}
3549

3650
[Fact]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Cnblogs.Architecture.IntegrationTests;
2+
3+
public enum SubscribeType
4+
{
5+
None,
6+
ByEvent,
7+
ByEventAssemblies,
8+
ByEventHandler,
9+
ByEventHandlerAssemblies,
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
using Cnblogs.Architecture.Ddd.EventBus.Abstractions;
2+
3+
namespace Cnblogs.Architecture.TestIntegrationEvents;
4+
5+
public record BlogPostCreatedIntegrationEvent(Guid Id, DateTimeOffset CreatedTime, string Title) : IntegrationEvent(Id, CreatedTime);

0 commit comments

Comments
 (0)