From 226bf19af7007e8903e4b8811b60d479c26ccb39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 21 Feb 2019 20:52:03 +0100 Subject: [PATCH] #218 - Using event and exception to handle success and failure. --- .../Bootstrap/BootstrapTask.cs | 2 +- .../Commands/Handlers/UserHandler.cs | 19 +++++- src/Money.UI.Backend/Hubs/ApiHub.cs | 5 +- .../Commands/PasswordChangeFailedException.cs | 15 +++++ src/Money.UI.Blazor/Events/PasswordChanged.cs | 20 ++++++ .../Pages/User/ChangePassword.cshtml | 15 +---- .../Pages/User/ChangePassword.cshtml.cs | 67 +++++++++++++++++++ 7 files changed, 125 insertions(+), 18 deletions(-) create mode 100644 src/Money.UI.Blazor/Commands/PasswordChangeFailedException.cs create mode 100644 src/Money.UI.Blazor/Events/PasswordChanged.cs create mode 100644 src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml.cs diff --git a/src/Money.UI.Backend/Bootstrap/BootstrapTask.cs b/src/Money.UI.Backend/Bootstrap/BootstrapTask.cs index 1b205555..f732fe35 100644 --- a/src/Money.UI.Backend/Bootstrap/BootstrapTask.cs +++ b/src/Money.UI.Backend/Bootstrap/BootstrapTask.cs @@ -179,7 +179,7 @@ private void Domain() bootstrapTask.Initialize(); - commandDispatcher.Handlers.AddAll(new UserHandler(services.BuildServiceProvider().GetRequiredService>())); + commandDispatcher.Handlers.AddAll(new UserHandler(services.BuildServiceProvider().GetRequiredService>(), eventDispatcher)); } private void ReadModels() diff --git a/src/Money.UI.Backend/Commands/Handlers/UserHandler.cs b/src/Money.UI.Backend/Commands/Handlers/UserHandler.cs index 5fe84a64..206836ad 100644 --- a/src/Money.UI.Backend/Commands/Handlers/UserHandler.cs +++ b/src/Money.UI.Backend/Commands/Handlers/UserHandler.cs @@ -1,8 +1,11 @@ using Microsoft.AspNetCore.Identity; +using Money.Events; using Money.Models; using Neptuo; using Neptuo.Collections.Specialized; using Neptuo.Commands.Handlers; +using Neptuo.Events; +using Neptuo.Models; using Neptuo.Models.Keys; using System; using System.Collections.Generic; @@ -15,11 +18,14 @@ namespace Money.Commands.Handlers public class UserHandler : ICommandHandler> { private readonly UserManager userManager; + private readonly IEventDispatcher eventDispatcher; - public UserHandler(UserManager userManager) + public UserHandler(UserManager userManager, IEventDispatcher eventDispatcher) { Ensure.NotNull(userManager, "userManager"); + Ensure.NotNull(eventDispatcher, "eventDispatcher"); this.userManager = userManager; + this.eventDispatcher = eventDispatcher; } public async Task HandleAsync(Envelope command) @@ -31,7 +37,16 @@ public async Task HandleAsync(Envelope command) IdentityResult result = await userManager.ChangePasswordAsync(user, command.Body.Current, command.Body.New); if (!result.Succeeded) - throw new InvalidOperationException($"Password change failed for ID '{userKey.Identifier}'."); + { + var ex = new PasswordChangeFailedException(); + AggregateRootExceptionDecorator decorator = new AggregateRootExceptionDecorator(ex); + decorator.SetCommandKey(command.Body.Key); + decorator.SetSourceCommandKey(command.Body.Key); + decorator.SetKey(userKey); + throw ex; + } + + await eventDispatcher.PublishAsync(new PasswordChanged(GuidKey.Create(Guid.NewGuid(), "PasswordChanged"), userKey)); } } } diff --git a/src/Money.UI.Backend/Hubs/ApiHub.cs b/src/Money.UI.Backend/Hubs/ApiHub.cs index 42626904..051e0a73 100644 --- a/src/Money.UI.Backend/Hubs/ApiHub.cs +++ b/src/Money.UI.Backend/Hubs/ApiHub.cs @@ -25,7 +25,8 @@ public class ApiHub : Hub, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, - IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler + IEventHandler, IEventHandler, IEventHandler, IEventHandler, IEventHandler, + IEventHandler { private readonly FormatterContainer formatters; @@ -126,6 +127,8 @@ private Task RaiseEvent(T payload) Task IEventHandler.HandleAsync(OutcomeDescriptionChanged payload) => RaiseEvent(payload); Task IEventHandler.HandleAsync(OutcomeWhenChanged payload) => RaiseEvent(payload); + Task IEventHandler.HandleAsync(PasswordChanged payload) => RaiseEvent(payload); + public void Handle(AggregateRootException exception) { string type = exception.GetType().AssemblyQualifiedName; diff --git a/src/Money.UI.Blazor/Commands/PasswordChangeFailedException.cs b/src/Money.UI.Blazor/Commands/PasswordChangeFailedException.cs new file mode 100644 index 00000000..393f4e9a --- /dev/null +++ b/src/Money.UI.Blazor/Commands/PasswordChangeFailedException.cs @@ -0,0 +1,15 @@ +using Neptuo.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Money.Commands +{ + /// + /// An exception raised when user password change fails. + /// + public class PasswordChangeFailedException : AggregateRootException + { } +} diff --git a/src/Money.UI.Blazor/Events/PasswordChanged.cs b/src/Money.UI.Blazor/Events/PasswordChanged.cs new file mode 100644 index 00000000..e58ec8c9 --- /dev/null +++ b/src/Money.UI.Blazor/Events/PasswordChanged.cs @@ -0,0 +1,20 @@ +using Neptuo.Events; +using Neptuo.Models.Keys; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Money.Events +{ + /// + /// An event raised when user password has been changed. + /// + public class PasswordChanged : Event + { + public PasswordChanged(IKey key, IKey aggregateKey) + : base(key, aggregateKey, 0) + { } + } +} diff --git a/src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml b/src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml index 509e5b02..df45bc25 100644 --- a/src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml +++ b/src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml @@ -1,5 +1,5 @@ @page "/user/changepassword" -@inject ICommandDispatcher Commands +@inherits ChangePasswordBase @@ -24,16 +24,3 @@ - -@functions -{ - public string Current { get; set; } - public string New { get; set; } - public string Confirm { get; set; } - - private async Task OnFormSubmit() - { - if (!String.IsNullOrEmpty(Current) && !String.IsNullOrEmpty(New) && New == Confirm) - await Commands.HandleAsync(new Commands.ChangePassword(Current, New)); - } -} \ No newline at end of file diff --git a/src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml.cs b/src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml.cs new file mode 100644 index 00000000..2ec3560a --- /dev/null +++ b/src/Money.UI.Blazor/Pages/User/ChangePassword.cshtml.cs @@ -0,0 +1,67 @@ +using Microsoft.AspNetCore.Blazor.Components; +using Money.Events; +using Neptuo.Commands; +using Neptuo.Events; +using Neptuo.Events.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Money.Pages +{ + public class ChangePasswordBase : BlazorComponent, IEventHandler, IDisposable + { + [Inject] + public ICommandDispatcher Commands { get; set; } + + [Inject] + public IEventHandlerCollection EventHandlers { get; set; } + + public string Current { get; set; } + public string New { get; set; } + public string Confirm { get; set; } + + protected override Task OnInitAsync() + { + BindEvents(); + return base.OnInitAsync(); + } + + protected async Task OnFormSubmit() + { + if (!String.IsNullOrEmpty(Current) && !String.IsNullOrEmpty(New) && New == Confirm) + await Commands.HandleAsync(new Commands.ChangePassword(Current, New)); + } + + public void Dispose() + { + UnBindEvents(); + } + + #region Events + + private void BindEvents() + { + EventHandlers + .Add(this); + } + + private void UnBindEvents() + { + EventHandlers + .Remove(this); + } + + Task IEventHandler.HandleAsync(PasswordChanged payload) + { + Current = null; + New = null; + Confirm = null; + return Task.CompletedTask; + } + + #endregion + } +}