-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11199 from umbraco/register-notification-extension
v9: Added Extension method to register all custom Notifications
- Loading branch information
Showing
2 changed files
with
187 additions
and
0 deletions.
There are no files selected for viewing
104 changes: 104 additions & 0 deletions
104
src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Umbraco.Cms.Core.DependencyInjection; | ||
using Umbraco.Cms.Core.Events; | ||
|
||
namespace Umbraco.Cms.Core.Extensions | ||
{ | ||
public static class UmbracoBuilderExtensions | ||
{ | ||
/// <summary> | ||
/// Registers all <see cref="INotificationHandler{TNotification}"/> within an assembly | ||
/// </summary> | ||
/// <param name="self"><see cref="IUmbracoBuilder"/></param> | ||
/// <typeparam name="T">Type contained within the targeted assembly</typeparam> | ||
/// <returns></returns> | ||
public static IUmbracoBuilder AddNotificationsFromAssembly<T>(this IUmbracoBuilder self) | ||
{ | ||
AddNotificationHandlers<T>(self); | ||
AddAsyncNotificationHandlers<T>(self); | ||
|
||
return self; | ||
} | ||
|
||
private static void AddNotificationHandlers<T>(IUmbracoBuilder self) | ||
{ | ||
var notificationHandlers = GetNotificationHandlers<T>(); | ||
foreach (var notificationHandler in notificationHandlers) | ||
{ | ||
var handlerImplementations = GetNotificationHandlerImplementations<T>(notificationHandler); | ||
foreach (var implementation in handlerImplementations) | ||
{ | ||
RegisterNotificationHandler(self, implementation, notificationHandler); | ||
} | ||
} | ||
} | ||
|
||
private static List<Type> GetNotificationHandlers<T>() => | ||
typeof(T).Assembly.GetTypes() | ||
.Where(x => x.IsAssignableToGenericType(typeof(INotificationHandler<>))) | ||
.ToList(); | ||
|
||
private static List<Type> GetNotificationHandlerImplementations<T>(Type handlerType) => | ||
handlerType | ||
.GetInterfaces() | ||
.Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(INotificationHandler<>)) | ||
.ToList(); | ||
|
||
private static void AddAsyncNotificationHandlers<T>(IUmbracoBuilder self) | ||
{ | ||
var notificationHandlers = GetAsyncNotificationHandlers<T>(); | ||
foreach (var notificationHandler in notificationHandlers) | ||
{ | ||
var handlerImplementations = GetAsyncNotificationHandlerImplementations<T>(notificationHandler); | ||
foreach (var handler in handlerImplementations) | ||
{ | ||
RegisterNotificationHandler(self, handler, notificationHandler); | ||
} | ||
} | ||
} | ||
|
||
private static List<Type> GetAsyncNotificationHandlers<T>() => | ||
typeof(T).Assembly.GetTypes() | ||
.Where(x => x.IsAssignableToGenericType(typeof(INotificationAsyncHandler<>))) | ||
.ToList(); | ||
|
||
private static List<Type> GetAsyncNotificationHandlerImplementations<T>(Type handlerType) => | ||
handlerType | ||
.GetInterfaces() | ||
.Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(INotificationAsyncHandler<>)) | ||
.ToList(); | ||
|
||
private static void RegisterNotificationHandler(IUmbracoBuilder self, Type notificationHandlerType, Type implementingHandlerType) | ||
{ | ||
var descriptor = new UniqueServiceDescriptor(notificationHandlerType, implementingHandlerType, ServiceLifetime.Transient); | ||
if (!self.Services.Contains(descriptor)) | ||
{ | ||
self.Services.Add(descriptor); | ||
} | ||
} | ||
|
||
private static bool IsAssignableToGenericType(this Type givenType, Type genericType) | ||
{ | ||
var interfaceTypes = givenType.GetInterfaces(); | ||
|
||
foreach (var it in interfaceTypes) | ||
{ | ||
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) | ||
{ | ||
return true; | ||
} | ||
|
||
var baseType = givenType.BaseType; | ||
return baseType != null && IsAssignableToGenericType(baseType, genericType); | ||
} | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UmbracoBuilderExtensionsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using AutoFixture; | ||
using AutoFixture.NUnit3; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
using NUnit.Framework; | ||
using Umbraco.Cms.Core.Cache; | ||
using Umbraco.Cms.Core.Composing; | ||
using Umbraco.Cms.Core.DependencyInjection; | ||
using Umbraco.Cms.Core.Events; | ||
using Umbraco.Cms.Core.Extensions; | ||
using Umbraco.Cms.Core.Hosting; | ||
using Umbraco.Cms.Core.Logging; | ||
using Umbraco.Cms.Core.Notifications; | ||
|
||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions | ||
{ | ||
[TestFixture] | ||
public class UmbracoBuilderExtensionsTests | ||
{ | ||
[Test, Customization] | ||
public void AddNotificationsFromAssembly_Should_AddNotificationHandler_To_ServicesCollection(IUmbracoBuilder sut) | ||
{ | ||
sut.AddNotificationsFromAssembly<CustomizationAttribute>(); | ||
|
||
var expectedHandlerType = typeof(INotificationHandler<ContentPublishedNotification>); | ||
var handler = sut.Services.SingleOrDefault(x => x.ServiceType == expectedHandlerType); | ||
Assert.NotNull(handler); | ||
Assert.That(handler.ImplementationType, Is.EqualTo(typeof(StubNotificationHandler))); | ||
} | ||
|
||
[Test, Customization] | ||
public void AddNotificationsFromAssembly_Should_AddAsyncNotificationHandler_To_ServicesCollection(IUmbracoBuilder sut) | ||
{ | ||
sut.AddNotificationsFromAssembly<CustomizationAttribute>(); | ||
|
||
var expectedHandlerType = typeof(INotificationAsyncHandler<ContentPublishedNotification>); | ||
var handler = sut.Services.SingleOrDefault(x => x.ServiceType == expectedHandlerType); | ||
Assert.NotNull(handler); | ||
Assert.That(handler.ImplementationType, Is.EqualTo(typeof(StubNotificationHandler))); | ||
} | ||
|
||
private class CustomizationAttribute : AutoDataAttribute | ||
{ | ||
public CustomizationAttribute() : base(() => { | ||
var fixture = new Fixture(); | ||
|
||
var stub = new UmbracoBuildStub(); | ||
fixture.Inject((IUmbracoBuilder)stub); | ||
|
||
return fixture; | ||
}){} | ||
} | ||
|
||
private class UmbracoBuildStub : IUmbracoBuilder | ||
{ | ||
public IServiceCollection Services { get; } | ||
public IConfiguration Config { get; } | ||
public TypeLoader TypeLoader { get; } | ||
public ILoggerFactory BuilderLoggerFactory { get; } | ||
public IHostingEnvironment BuilderHostingEnvironment { get; } | ||
public IProfiler Profiler { get; } | ||
public AppCaches AppCaches { get; } | ||
public TBuilder WithCollectionBuilder<TBuilder>() where TBuilder : ICollectionBuilder, new() => default; | ||
|
||
public UmbracoBuildStub() => Services = new ServiceCollection(); | ||
|
||
public void Build() {} | ||
} | ||
|
||
private class StubNotificationHandler | ||
: INotificationHandler<ContentPublishedNotification> | ||
, INotificationAsyncHandler<ContentPublishedNotification> | ||
{ | ||
public void Handle(ContentPublishedNotification notification) { } | ||
|
||
public Task HandleAsync(ContentPublishedNotification notification, CancellationToken cancellationToken) => Task.CompletedTask; | ||
} | ||
} | ||
} |