From fd576e40a5b40ba906c5a025cdf4484f029d1e2d Mon Sep 17 00:00:00 2001 From: Timo Notheisen <65653426+tnotheis@users.noreply.github.com> Date: Mon, 6 May 2024 10:26:26 +0200 Subject: [PATCH] Event Handler: Do not create external events for identity deletion if change was initiated by owner (#638) * feat: don't create an external event in case the deletion process was started by the identity * feat: don't create an external event in case the deletion process status was changed by the owner of the identity --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../CancelDeletionProcessAsOwner/Handler.cs | 2 +- .../CancelDeletionProcessAsSupport/Handler.cs | 2 +- .../Handler.cs | 2 +- .../StartDeletionProcessAsSupport/Handler.cs | 2 +- ...entityDeletionProcessStartedDomainEvent.cs | 4 +- ...DeletionProcessStatusChangedDomainEvent.cs | 4 +- ...onProcessStartedDomainEventHandlerTests.cs | 2 +- ...eletionProcessStartedDomainEventHandler.cs | 4 ++ ...nProcessStatusChangedDomainEventHandler.cs | 4 ++ ...entityDeletionProcessStartedDomainEvent.cs | 4 +- ...DeletionProcessStatusChangedDomainEvent.cs | 4 +- ...onProcessStartedDomainEventHandlerTests.cs | 34 ++++++++++- ...essStatusChangedDomainEventHandlerTests.cs | 57 ++++++++++++++----- 13 files changed, 100 insertions(+), 25 deletions(-) diff --git a/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsOwner/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsOwner/Handler.cs index 15427d761e..89bc8b28c7 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsOwner/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsOwner/Handler.cs @@ -41,7 +41,7 @@ public async Task Handle(CancelDeletionPro var newTierId = identity.TierId; _eventBus.Publish(new TierOfIdentityChangedDomainEvent(identity, oldTierId, newTierId)); - _eventBus.Publish(new IdentityDeletionProcessStatusChangedDomainEvent(identity.Address, deletionProcess.Id)); + _eventBus.Publish(new IdentityDeletionProcessStatusChangedDomainEvent(identity.Address, deletionProcess.Id, _userContext.GetAddress())); return new CancelDeletionProcessAsOwnerResponse(deletionProcess); } diff --git a/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsSupport/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsSupport/Handler.cs index fed088842a..80ef53d673 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsSupport/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Commands/CancelDeletionProcessAsSupport/Handler.cs @@ -37,7 +37,7 @@ public async Task Handle(CancelDeletionAsSuppor var newTierId = identity.TierId; _eventBus.Publish(new TierOfIdentityChangedDomainEvent(identity, oldTierId, newTierId)); - _eventBus.Publish(new IdentityDeletionProcessStatusChangedDomainEvent(identity.Address, deletionProcess.Id)); + _eventBus.Publish(new IdentityDeletionProcessStatusChangedDomainEvent(identity.Address, deletionProcess.Id, null)); return new CancelDeletionAsSupportResponse(deletionProcess); } diff --git a/Modules/Devices/src/Devices.Application/Identities/Commands/CancelStaleIdentityDeletionProcesses/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Commands/CancelStaleIdentityDeletionProcesses/Handler.cs index a835059b0d..37970841a4 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Commands/CancelStaleIdentityDeletionProcesses/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Commands/CancelStaleIdentityDeletionProcesses/Handler.cs @@ -34,7 +34,7 @@ public async Task Handle(CancelSta await _identityRepository.Update(identity, cancellationToken); - _eventBus.Publish(new IdentityDeletionProcessStatusChangedDomainEvent(identity.Address, deletionProcess.Value.Id)); + _eventBus.Publish(new IdentityDeletionProcessStatusChangedDomainEvent(identity.Address, deletionProcess.Value.Id, null)); } return new CancelStaleIdentityDeletionProcessesResponse(idsOfCancelledDeletionProcesses); diff --git a/Modules/Devices/src/Devices.Application/Identities/Commands/StartDeletionProcessAsSupport/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Commands/StartDeletionProcessAsSupport/Handler.cs index ee2e9e9365..9406345c5d 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Commands/StartDeletionProcessAsSupport/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Commands/StartDeletionProcessAsSupport/Handler.cs @@ -25,7 +25,7 @@ public async Task Handle(StartDeletionPro await _identitiesRepository.Update(identity, cancellationToken); - _eventBus.Publish(new IdentityDeletionProcessStartedDomainEvent(identity.Address, deletionProcess.Id)); + _eventBus.Publish(new IdentityDeletionProcessStartedDomainEvent(identity.Address, deletionProcess.Id, null)); return new StartDeletionProcessAsSupportResponse(deletionProcess); } diff --git a/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStartedDomainEvent.cs b/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStartedDomainEvent.cs index a0f2984d7f..527e4f5c3b 100644 --- a/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStartedDomainEvent.cs +++ b/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStartedDomainEvent.cs @@ -4,12 +4,14 @@ namespace Backbone.Modules.Devices.Domain.DomainEvents.Outgoing; public class IdentityDeletionProcessStartedDomainEvent : DomainEvent { - public IdentityDeletionProcessStartedDomainEvent(string identityAddress, string deletionProcessId) : base($"{identityAddress}/DeletionProcessStarted/{deletionProcessId}") + public IdentityDeletionProcessStartedDomainEvent(string identityAddress, string deletionProcessId, string? initiator) : base($"{identityAddress}/DeletionProcessStarted/{deletionProcessId}") { DeletionProcessId = deletionProcessId; Address = identityAddress; + Initiator = initiator; } public string Address { get; private set; } public string DeletionProcessId { get; private set; } + public string? Initiator { get; set; } } diff --git a/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStatusChangedDomainEvent.cs b/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStatusChangedDomainEvent.cs index f08ce41eb2..f67f1ecb58 100644 --- a/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStatusChangedDomainEvent.cs +++ b/Modules/Devices/src/Devices.Domain/DomainEvents/Outgoing/IdentityDeletionProcessStatusChangedDomainEvent.cs @@ -4,13 +4,15 @@ namespace Backbone.Modules.Devices.Domain.DomainEvents.Outgoing; public class IdentityDeletionProcessStatusChangedDomainEvent : DomainEvent { - public IdentityDeletionProcessStatusChangedDomainEvent(string identityAddress, string deletionProcessId) + public IdentityDeletionProcessStatusChangedDomainEvent(string identityAddress, string deletionProcessId, string? initiator) : base($"{identityAddress}/IdentityDeletionProcessStatusChanged/{deletionProcessId}") { Address = identityAddress; DeletionProcessId = deletionProcessId; + Initiator = initiator; } public string Address { get; } public string DeletionProcessId { get; } + public string? Initiator { get; } } diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs index 1c1d9a7284..4172aac0e7 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs @@ -17,7 +17,7 @@ public async Task Sends_push_notification() var mockPushNotificationSender = A.Fake(); var handler = new IdentityDeletionProcessStartedDomainEventHandler(mockPushNotificationSender); var identity = TestDataGenerator.CreateIdentity(); - var identityDeletionProcessStartedDomainEvent = new IdentityDeletionProcessStartedDomainEvent(identity.Address, IdentityDeletionProcess.StartAsSupport(identity.Address).Id); + var identityDeletionProcessStartedDomainEvent = new IdentityDeletionProcessStartedDomainEvent(identity.Address, IdentityDeletionProcess.StartAsSupport(identity.Address).Id, null); // Act await handler.Handle(identityDeletionProcessStartedDomainEvent); diff --git a/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEventHandler.cs b/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEventHandler.cs index 4595fcaf55..1ca5ad60f7 100644 --- a/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEventHandler.cs +++ b/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEventHandler.cs @@ -23,6 +23,10 @@ public IdentityDeletionProcessStartedDomainEventHandler(ISynchronizationDbContex public async Task Handle(IdentityDeletionProcessStartedDomainEvent domainEvent) { + // No need to create an external event if the deletion process was started by the identity itself (in that case it's not "external"). + if (domainEvent.Initiator == domainEvent.Address) + return; + #pragma warning disable IDE0037 var payload = new { DeletionProcessId = domainEvent.DeletionProcessId }; #pragma warning restore IDE0037 diff --git a/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEventHandler.cs b/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEventHandler.cs index 5914699d82..2c97609b5d 100644 --- a/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEventHandler.cs +++ b/Modules/Synchronization/src/Synchronization.Application/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEventHandler.cs @@ -25,6 +25,10 @@ public IdentityDeletionProcessStatusChangedDomainEventHandler(ISynchronizationDb public async Task Handle(IdentityDeletionProcessStatusChangedDomainEvent @event) { + // No need to create an external event if the action that triggered the event was initiated by the owner of the deletion process (in that case it's not "external"). + if (@event.Initiator == @event.Address) + return; + #pragma warning disable IDE0037 var payload = new { DeletionProcessId = @event.DeletionProcessId }; #pragma warning restore IDE0037 diff --git a/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEvent.cs b/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEvent.cs index c67eb273e4..9ecb15575b 100644 --- a/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEvent.cs +++ b/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStarted/IdentityDeletionProcessStartedDomainEvent.cs @@ -4,12 +4,14 @@ namespace Backbone.Modules.Synchronization.Domain.DomainEvents.Incoming.Identity public class IdentityDeletionProcessStartedDomainEvent : DomainEvent { - public IdentityDeletionProcessStartedDomainEvent(string identityAddress, string deletionProcessId) : base($"{identityAddress}/DeletionProcessStarted/{deletionProcessId}") + public IdentityDeletionProcessStartedDomainEvent(string identityAddress, string deletionProcessId, string? initiator) { DeletionProcessId = deletionProcessId; Address = identityAddress; + Initiator = initiator; } public string Address { get; private set; } public string DeletionProcessId { get; private set; } + public string? Initiator { get; } } diff --git a/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEvent.cs b/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEvent.cs index 4ea152df23..cbaf51fc92 100644 --- a/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEvent.cs +++ b/Modules/Synchronization/src/Synchronization.Domain/DomainEvents/Incoming/IdentityDeletionProcessStatusChanged/IdentityDeletionProcessStatusChangedDomainEvent.cs @@ -4,12 +4,14 @@ namespace Backbone.Modules.Synchronization.Domain.DomainEvents.Incoming.Identity public class IdentityDeletionProcessStatusChangedDomainEvent : DomainEvent { - public IdentityDeletionProcessStatusChangedDomainEvent(string address, string deletionProcessId) + public IdentityDeletionProcessStatusChangedDomainEvent(string address, string deletionProcessId, string? initiator) { Address = address; DeletionProcessId = deletionProcessId; + Initiator = initiator; } public string Address { get; } public string DeletionProcessId { get; } + public string? Initiator { get; } } diff --git a/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs b/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs index a18d00211f..b89cfbd9b3 100644 --- a/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs +++ b/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStartedDomainEventHandlerTests.cs @@ -14,11 +14,11 @@ namespace Backbone.Modules.Synchronization.Application.Tests.Tests.DomainEvents; public class IdentityDeletionProcessStartedDomainEventHandlerTests { [Fact] - public async Task Creates_an_external_event() + public async Task Creates_an_external_event_if_initiator_is_someone_else() { // Arrange var identityAddress = TestDataGenerator.CreateRandomIdentityAddress(); - var identityDeletionProcessStartedDomainEvent = new IdentityDeletionProcessStartedDomainEvent(identityAddress, "some-deletion-process-id"); + var identityDeletionProcessStartedDomainEvent = new IdentityDeletionProcessStartedDomainEvent(identityAddress, "some-deletion-process-id", null); var fakeDbContext = A.Fake(); var mockEventBus = A.Fake(); @@ -37,9 +37,37 @@ public async Task Creates_an_external_event() // Act await handler.Handle(identityDeletionProcessStartedDomainEvent); - // Handle + // Assert A.CallTo(() => mockEventBus.Publish( A.That.Matches(e => e.Owner == externalEvent.Owner && e.EventId == externalEvent.Id)) ).MustHaveHappenedOnceExactly(); } + + [Fact] + public async Task Does_nothing_if_initiator_is_deletion_process_owner() + { + // Arrange + var deletionProcessOwner = TestDataGenerator.CreateRandomIdentityAddress(); + var identityDeletionProcessStartedDomainEvent = new IdentityDeletionProcessStartedDomainEvent(deletionProcessOwner, "some-deletion-process-id", deletionProcessOwner); + + var fakeDbContext = A.Fake(); + var mockEventBus = A.Fake(); + + var externalEvent = new ExternalEvent(ExternalEventType.IdentityDeletionProcessStarted, IdentityAddress.Parse(deletionProcessOwner), 1, + new { identityDeletionProcessStartedDomainEvent.DeletionProcessId }); + + A.CallTo(() => fakeDbContext.CreateExternalEvent( + A.That.Matches(i => i.Value == deletionProcessOwner), + ExternalEventType.IdentityDeletionProcessStarted, + A._) + ).Returns(externalEvent); + + var handler = new IdentityDeletionProcessStartedDomainEventHandler(fakeDbContext, mockEventBus, A.Fake>()); + + // Act + await handler.Handle(identityDeletionProcessStartedDomainEvent); + + // Assert + A.CallTo(() => mockEventBus.Publish(A._)).MustNotHaveHappened(); + } } diff --git a/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStatusChangedDomainEventHandlerTests.cs b/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStatusChangedDomainEventHandlerTests.cs index 82272696e0..c02740f7c1 100644 --- a/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStatusChangedDomainEventHandlerTests.cs +++ b/Modules/Synchronization/test/Synchronization.Application.Tests/Tests/DomainEvents/IdentityDeletionProcessStatusChangedDomainEventHandlerTests.cs @@ -14,19 +14,19 @@ namespace Backbone.Modules.Synchronization.Application.Tests.Tests.DomainEvents; public class IdentityDeletionProcessStatusChangedDomainEventHandlerTests { [Fact] - public async Task Creates_an_external_event() + public async Task Creates_an_external_event_if_initiator_is_someone_else() { // Arrange - var identityAddress = TestDataGenerator.CreateRandomIdentityAddress(); - var identityDeletionProcessStatusChangedDomainEvent = new IdentityDeletionProcessStatusChangedDomainEvent(identityAddress, "someDeletionProcessId"); + var deletionProcessOwner = TestDataGenerator.CreateRandomIdentityAddress(); + var identityDeletionProcessStatusChangedDomainEvent = new IdentityDeletionProcessStatusChangedDomainEvent(deletionProcessOwner, "someDeletionProcessId", null); var mockDbContext = A.Fake(); - var externalEvent = new ExternalEvent(ExternalEventType.IdentityDeletionProcessStatusChanged, IdentityAddress.Parse(identityAddress), 1, + var externalEvent = new ExternalEvent(ExternalEventType.IdentityDeletionProcessStatusChanged, IdentityAddress.Parse(deletionProcessOwner), 1, new { identityDeletionProcessStatusChangedDomainEvent.DeletionProcessId }); A.CallTo(() => mockDbContext.CreateExternalEvent( - identityAddress, + deletionProcessOwner, ExternalEventType.IdentityDeletionProcessStatusChanged, A._) ).Returns(externalEvent); @@ -38,26 +38,26 @@ public async Task Creates_an_external_event() // Act await handler.Handle(identityDeletionProcessStatusChangedDomainEvent); - // Handle - A.CallTo(() => mockDbContext.CreateExternalEvent(identityAddress, ExternalEventType.IdentityDeletionProcessStatusChanged, A._)) + // Assert + A.CallTo(() => mockDbContext.CreateExternalEvent(deletionProcessOwner, ExternalEventType.IdentityDeletionProcessStatusChanged, A._)) .MustHaveHappenedOnceExactly(); } [Fact] - public async Task Publishes_an_external_event() + public async Task Publishes_a_domain_event_after_creating_an_external_event() { // Arrange - var identityAddress = TestDataGenerator.CreateRandomIdentityAddress(); - var identityDeletionProcessStatusChangedDomainEvent = new IdentityDeletionProcessStatusChangedDomainEvent(identityAddress, "someDeletionProcessId"); + var deletionProcessOwner = TestDataGenerator.CreateRandomIdentityAddress(); + var identityDeletionProcessStatusChangedDomainEvent = new IdentityDeletionProcessStatusChangedDomainEvent(deletionProcessOwner, "someDeletionProcessId", null); var fakeDbContext = A.Fake(); var mockEventBus = A.Fake(); - var externalEvent = new ExternalEvent(ExternalEventType.IdentityDeletionProcessStatusChanged, IdentityAddress.Parse(identityAddress), 1, + var externalEvent = new ExternalEvent(ExternalEventType.IdentityDeletionProcessStatusChanged, IdentityAddress.Parse(deletionProcessOwner), 1, new { identityDeletionProcessStatusChangedDomainEvent.DeletionProcessId }); A.CallTo(() => fakeDbContext.CreateExternalEvent( - identityAddress, + deletionProcessOwner, ExternalEventType.IdentityDeletionProcessStatusChanged, A._) ).Returns(externalEvent); @@ -69,9 +69,40 @@ public async Task Publishes_an_external_event() // Act await handler.Handle(identityDeletionProcessStatusChangedDomainEvent); - // Handle + // Assert A.CallTo(() => mockEventBus.Publish( A.That.Matches(e => e.Owner == externalEvent.Owner && e.EventId == externalEvent.Id)) ).MustHaveHappenedOnceExactly(); } + + [Fact] + public async Task Does_nothing_if_initiator_is_deletion_process_owner() + { + // Arrange + var deletionProcessOwner = TestDataGenerator.CreateRandomIdentityAddress(); + var identityDeletionProcessStatusChangedDomainEvent = new IdentityDeletionProcessStatusChangedDomainEvent(deletionProcessOwner, "someDeletionProcessId", deletionProcessOwner); + + var mockDbContext = A.Fake(); + var mockEventBus = A.Fake(); + + var externalEvent = new ExternalEvent(ExternalEventType.IdentityDeletionProcessStatusChanged, IdentityAddress.Parse(deletionProcessOwner), 1, + new { identityDeletionProcessStatusChangedDomainEvent.DeletionProcessId }); + + A.CallTo(() => mockDbContext.CreateExternalEvent( + deletionProcessOwner, + ExternalEventType.IdentityDeletionProcessStatusChanged, + A._) + ).Returns(externalEvent); + + var handler = new IdentityDeletionProcessStatusChangedDomainEventHandler(mockDbContext, + mockEventBus, + A.Fake>()); + + // Act + await handler.Handle(identityDeletionProcessStatusChangedDomainEvent); + + // Assert + A.CallTo(() => mockDbContext.CreateExternalEvent(A._, A._, A._)).MustNotHaveHappened(); + A.CallTo(() => mockEventBus.Publish(A._)).MustNotHaveHappened(); + } }