Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v9: Added Extension method to register all custom Notifications #11199

Merged
merged 3 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs
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);
}
}
}
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;
}
}
}