From 649d7fd8290df00673923c8dbc06025d35adf8e3 Mon Sep 17 00:00:00 2001 From: Martin Humlund Clausen Date: Mon, 27 Sep 2021 08:28:40 +0200 Subject: [PATCH 1/3] Added UmbracoBuilderExtensions.AddNotificationsFromAssembly to help all notifications from within an Assembly --- .../Extensions/UmbracoBuilderExtensions.cs | 100 ++++++++++++++++++ .../UmbracoBuilderExtensionsTests.cs | 83 +++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UmbracoBuilderExtensionsTests.cs diff --git a/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs new file mode 100644 index 0000000000..145efe900a --- /dev/null +++ b/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs @@ -0,0 +1,100 @@ +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 + { + /// + /// Registers all within an assembly + /// + /// + /// Type contained within the targeted assembly + /// + public static IUmbracoBuilder AddNotificationsFromAssembly(this IUmbracoBuilder self) + { + AddNotificationHandlers(self); + AddAsyncNotificationHandlers(self); + + return self; + } + + private static void AddNotificationHandlers(IUmbracoBuilder self) + { + var notificationHandlers = GetNotificationHandlers(); + foreach (var notificationHandler in notificationHandlers) + { + var handlerImplementations = GetNotificationHandlerImplementations(notificationHandler); + foreach (var implementation in handlerImplementations) + { + RegisterNotificationHandler(self, implementation, notificationHandler); + } + } + } + + private static List GetNotificationHandlers() => + typeof(T).Assembly.GetTypes() + .Where(x => x.IsAssignableToGenericType(typeof(INotificationHandler<>))) + .ToList(); + + private static List GetNotificationHandlerImplementations(Type handlerType) => + handlerType + .GetInterfaces() + .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(INotificationHandler<>)) + .ToList(); + + private static void AddAsyncNotificationHandlers(IUmbracoBuilder self) + { + var notificationHandlers = GetAsyncNotificationHandlers(); + foreach (var notificationHandler in notificationHandlers) + { + var handlerImplementations = GetAsyncNotificationHandlerImplementations(notificationHandler); + foreach (var handler in handlerImplementations) + { + RegisterNotificationHandler(self, handler, notificationHandler); + } + } + } + + private static List GetAsyncNotificationHandlers() => + typeof(T).Assembly.GetTypes() + .Where(x => x.IsAssignableToGenericType(typeof(INotificationAsyncHandler<>))) + .ToList(); + + private static List GetAsyncNotificationHandlerImplementations(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); + } + } +} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UmbracoBuilderExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UmbracoBuilderExtensionsTests.cs new file mode 100644 index 0000000000..4894a15ba0 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UmbracoBuilderExtensionsTests.cs @@ -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(); + + var expectedHandlerType = typeof(INotificationHandler); + 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(); + + var expectedHandlerType = typeof(INotificationAsyncHandler); + 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() where TBuilder : ICollectionBuilder, new() => default; + + public UmbracoBuildStub() => Services = new ServiceCollection(); + + public void Build() {} + } + + private class StubNotificationHandler + : INotificationHandler + , INotificationAsyncHandler + { + public void Handle(ContentPublishedNotification notification) { } + + public Task HandleAsync(ContentPublishedNotification notification, CancellationToken cancellationToken) => Task.CompletedTask; + } + } +} From 0d7c6e412824430ff68fe4b9b009a63467d6dbff Mon Sep 17 00:00:00 2001 From: Martin Humlund Clausen Date: Sun, 17 Oct 2021 14:13:43 +0200 Subject: [PATCH 2/3] Update src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --- src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs index 145efe900a..a8fac86a9e 100644 --- a/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs @@ -87,7 +87,9 @@ namespace Umbraco.Cms.Core.Extensions foreach (var it in interfaceTypes) { if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType) + { return true; + } } if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) From 58d980cc80a0be400ccbe5409b274e56abb18e5f Mon Sep 17 00:00:00 2001 From: Martin Humlund Clausen Date: Sun, 17 Oct 2021 14:13:51 +0200 Subject: [PATCH 3/3] Update src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --- src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs index a8fac86a9e..5b4e3a92d9 100644 --- a/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Core/Extensions/UmbracoBuilderExtensions.cs @@ -92,8 +92,10 @@ namespace Umbraco.Cms.Core.Extensions } } - if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) + if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) + { return true; + } var baseType = givenType.BaseType; return baseType != null && IsAssignableToGenericType(baseType, genericType);