From 49f9b99ee1224c9f0aef8cc9cf56d395a90af1fb Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Tue, 11 Mar 2025 00:28:06 +0100 Subject: [PATCH] Adds Decorator, wip --- Core/Database/DatabaseScope.cs | 8 ++- Core/ModuleRegistry/SecurityModule.cs | 14 +++++ PlanTempus.Components/CommandHandler.cs | 4 +- PlanTempus.Components/ICommandHandler.cs | 15 ++++++ .../ModuleRegistry/CommandModule.cs | 36 ++++++++++--- .../Users/Create/CreateUserHandler.cs | 11 ---- .../Create/CreateUserHandlerDecorator.cs | 52 ++++++++++++------- Tests/CommandQueryHandlerTests/HandlerTest.cs | 12 +++-- Tests/TestFixture.cs | 5 +- 9 files changed, 112 insertions(+), 45 deletions(-) create mode 100644 Core/ModuleRegistry/SecurityModule.cs create mode 100644 PlanTempus.Components/ICommandHandler.cs diff --git a/Core/Database/DatabaseScope.cs b/Core/Database/DatabaseScope.cs index 7d61480..40db7c4 100644 --- a/Core/Database/DatabaseScope.cs +++ b/Core/Database/DatabaseScope.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Diagnostics; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; @@ -6,19 +7,24 @@ namespace PlanTempus.Core.Database; public class DatabaseScope : IDisposable { - private readonly IOperationHolder _operation; + internal readonly IOperationHolder _operation; + private readonly Stopwatch _stopwatch; public DatabaseScope(IDbConnection connection, IOperationHolder operation) { Connection = connection; _operation = operation; _operation.Telemetry.Success = true; + _stopwatch = Stopwatch.StartNew(); } public IDbConnection Connection { get; } public void Dispose() { + _stopwatch.Stop(); + _operation.Telemetry.Duration = _stopwatch.Elapsed; + _operation.Dispose(); Connection.Dispose(); } diff --git a/Core/ModuleRegistry/SecurityModule.cs b/Core/ModuleRegistry/SecurityModule.cs new file mode 100644 index 0000000..33be4a1 --- /dev/null +++ b/Core/ModuleRegistry/SecurityModule.cs @@ -0,0 +1,14 @@ +using Autofac; +using PlanTempus.Core.SeqLogging; + +namespace PlanTempus.Core.ModuleRegistry +{ + public class SecurityModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType() + .As(); + } + } +} \ No newline at end of file diff --git a/PlanTempus.Components/CommandHandler.cs b/PlanTempus.Components/CommandHandler.cs index 36d534d..85248e1 100644 --- a/PlanTempus.Components/CommandHandler.cs +++ b/PlanTempus.Components/CommandHandler.cs @@ -5,9 +5,9 @@ namespace PlanTempus.Components; public class CommandHandler(IComponentContext context) : ICommandHandler { - public Task Handle(TCommand command) + public async Task Handle(TCommand command) { var handler = context.Resolve>(); - return handler.Handle(command); + return await handler.Handle(command); } } \ No newline at end of file diff --git a/PlanTempus.Components/ICommandHandler.cs b/PlanTempus.Components/ICommandHandler.cs new file mode 100644 index 0000000..6eb35d7 --- /dev/null +++ b/PlanTempus.Components/ICommandHandler.cs @@ -0,0 +1,15 @@ +namespace PlanTempus.Components; + +public interface ICommandHandler +{ + Task Handle(TCommand command); +} + +public interface ICommandHandler +{ + Task Handle(T input); +} + +public interface ICommandHandlerDecorator +{ +} \ No newline at end of file diff --git a/PlanTempus.Components/ModuleRegistry/CommandModule.cs b/PlanTempus.Components/ModuleRegistry/CommandModule.cs index ff01a3a..4f153ec 100644 --- a/PlanTempus.Components/ModuleRegistry/CommandModule.cs +++ b/PlanTempus.Components/ModuleRegistry/CommandModule.cs @@ -1,4 +1,5 @@ using Autofac; +using PlanTempus.Components.Users.Create; using PlanTempus.Core.SeqLogging; namespace PlanTempus.Components.ModuleRegistry @@ -9,15 +10,34 @@ namespace PlanTempus.Components.ModuleRegistry protected override void Load(ContainerBuilder builder) { - // Registrer alle handlers - builder.RegisterAssemblyTypes() - .AsClosedTypesOf(typeof(ICommandHandler<>)) - .InstancePerLifetimeScope(); -// Registrer en decorator for alle ICommandHandler - builder.RegisterGenericDecorator( - typeof(CreateUserHandlerDecorator<>), // Din decorator-klasse - typeof(ICommandHandler<>)); // Interface, der skal dekoreres + builder.RegisterType() + .As() + .InstancePerLifetimeScope(); + + builder.RegisterAssemblyTypes(ThisAssembly) + .Where(t => !typeof(ICommandHandlerDecorator).IsAssignableFrom(t)) + .AsClosedTypesOf(typeof(ICommandHandler<,>)) + .InstancePerLifetimeScope(); + + + // Registrer alle handlers + // builder.RegisterAssemblyTypes(ThisAssembly) + // .AsClosedTypesOf(typeof(ICommandHandler<,>)) + // .InstancePerLifetimeScope(); + + builder.RegisterAssemblyTypes(ThisAssembly) + .As(); + + + builder.RegisterDecorator( + typeof(CreateUserHandlerDecorator), + typeof(ICommandHandler)); + // + // Registrer en decorator for alle ICommandHandler + // builder.RegisterGenericDecorator( + // typeof(CommandHandlerDecorator<,>), + // typeof(ICommandHandler<,>)); } } diff --git a/PlanTempus.Components/Users/Create/CreateUserHandler.cs b/PlanTempus.Components/Users/Create/CreateUserHandler.cs index af96227..39c63ff 100644 --- a/PlanTempus.Components/Users/Create/CreateUserHandler.cs +++ b/PlanTempus.Components/Users/Create/CreateUserHandler.cs @@ -8,16 +8,6 @@ using PlanTempus.Core.Telemetry; namespace PlanTempus.Components.Users.Create { - public interface ICommandHandler - { - Task Handle(TCommand command ); - } - - public interface ICommandHandler - { - Task Handle(T input); - } - public class CreateUserHandler( TelemetryClient telemetryClient, IDatabaseOperations databaseOperations, @@ -50,7 +40,6 @@ namespace PlanTempus.Components.Users.Create var result = data.First(); - telemetryClient.TrackTrace(GetType().Name, result.Format()); return result; } diff --git a/PlanTempus.Components/Users/Create/CreateUserHandlerDecorator.cs b/PlanTempus.Components/Users/Create/CreateUserHandlerDecorator.cs index 06445cf..8f9289c 100644 --- a/PlanTempus.Components/Users/Create/CreateUserHandlerDecorator.cs +++ b/PlanTempus.Components/Users/Create/CreateUserHandlerDecorator.cs @@ -1,25 +1,41 @@ -using Microsoft.ApplicationInsights; +using System.Diagnostics; +using Microsoft.ApplicationInsights; +using Microsoft.ApplicationInsights.DataContracts; namespace PlanTempus.Components.Users.Create; -public class CreateUserHandlerDecorator : ICommandHandler +public class CreateUserHandlerDecorator( + ICommandHandler decoratedHandler, + TelemetryClient telemetryClient) + : ICommandHandler, ICommandHandlerDecorator { - private readonly ICommandHandler _decoratedHandler; - private readonly TelemetryClient _telemetryClient; - - public CreateUserHandlerDecorator( - ICommandHandler decoratedHandler, - TelemetryClient telemetryClient) - { - _decoratedHandler = decoratedHandler; - _telemetryClient = telemetryClient; - } - public async Task Handle(CreateUserCommand command) { - _telemetryClient.TrackTrace($"Before handling {nameof(CreateUserCommand)}"); - var result = await _decoratedHandler.Handle(command); - _telemetryClient.TrackTrace($"After handling {nameof(CreateUserCommand)}"); - return result; + // var correlationId = Activity.Current?.RootId ?? command.CorrelationId; + + using (var operation = + telemetryClient.StartOperation($"Handle {nameof(CreateUserCommand)}", + command.CorrelationId.ToString())) + { + try + { + operation.Telemetry.Properties["CorrelationId"] = command.CorrelationId.ToString(); + var result = await decoratedHandler.Handle(command); + + operation.Telemetry.Success = true; + + return result; + } + catch (Exception ex) + { + operation.Telemetry.Success = false; + + telemetryClient.TrackException(ex, new Dictionary + { + ["CommandType"] = nameof(CreateUserCommand) + }); + throw; + } + } } -} +} \ No newline at end of file diff --git a/Tests/CommandQueryHandlerTests/HandlerTest.cs b/Tests/CommandQueryHandlerTests/HandlerTest.cs index dd77a0c..4d3c262 100644 --- a/Tests/CommandQueryHandlerTests/HandlerTest.cs +++ b/Tests/CommandQueryHandlerTests/HandlerTest.cs @@ -4,6 +4,7 @@ using PlanTempus.Components; using PlanTempus.Components.Users.Create; using PlanTempus.Core.Database; using PlanTempus.Core.Database.ConnectionFactory; +using Shouldly; namespace PlanTempus.X.TDD.CommandQueryHandlerTests; @@ -16,18 +17,21 @@ public class HandlerTest : TestFixture } [TestMethod] - public void TestDefaultConnection() + public async Task ShouldResolveCommandHandlerAndDispatchToGenericCommandHandler() { - var commandHandler = Container.Resolve(); + var commandHandler = Container.Resolve(); + commandHandler.ShouldBeOfType(); var command = new CreateUserCommand { - Email = "lloyd@dumbanddumber.com", // Lloyd Christmas + Email = "lloyd@dumbanddumber.com1", // Lloyd Christmas Password = "1234AceVentura#LOL", // Ace Ventura IsActive = true, CorrelationId = Guid.NewGuid() }; - var result = commandHandler.Handle(command); + var result = await commandHandler.Handle(command); + + result.ShouldNotBeNull(); } } \ No newline at end of file diff --git a/Tests/TestFixture.cs b/Tests/TestFixture.cs index 382279a..b1a7e66 100644 --- a/Tests/TestFixture.cs +++ b/Tests/TestFixture.cs @@ -2,6 +2,7 @@ using Autofac; using Microsoft.ApplicationInsights; using Microsoft.Extensions.Logging; +using PlanTempus.Components.ModuleRegistry; using PlanTempus.Core.Configurations; using PlanTempus.Core.Configurations.JsonConfigProvider; using PlanTempus.Core.ModuleRegistry; @@ -67,7 +68,9 @@ public abstract class TestFixture SeqConfiguration = configuration.GetSection("SeqConfiguration").ToObject() }); - + builder.RegisterModule(); + builder.RegisterModule(); + ContainerBuilder = builder; }