From 9dbd7854175f41aabd40740c3c378d8738f500b8 Mon Sep 17 00:00:00 2001 From: Hunor Tot-Bagi Date: Wed, 23 Oct 2024 13:43:51 +0300 Subject: [PATCH] Queueing of external events while identity is in status `ToBeDeleted` (#910) * feat: enable sending messages to peers that are in status `ToBeDeleted` * feat: allow changing relationships with `ToBeDeleted` peers * chore: update integration tests * feat: identities in status `ToBeDeleted` can't start a new sync run * refactor: remove address variable * feat: identities in status `ToBeDeleted` are not notified via push notifications * fix: actual deletion worker * test: fix tests * refactor: use void instead `IEventBus` * test: fix missing parameter * test: dont send notifications when identity is in status to be deleted * fix: relationship created by i1 * test: fix & improve tests * refactor: utilize `await` and remove `.Result` when finding an `Identity` * chore: test formatting * refactor: utilize `await` and rename application error * chore: formatting * chore: remove unused using * chore: remove unused unsings * chore: remove unused using * refactor: utilize `await` instead of `.Result` * chore: rename application error * chore: remove redundant identity status check * chore: formatting * chore: rename * chore: rename error message * chore: remove redundant `TestDataGenerator` * test: update test * refactor: status check * refactor: implement EnsureIdentityIsToBeDeleted * refactor: remove redundant method * refactor: implement not found exception * fix: unblock notifications * chore: refactor * test: improve tests * chore: rename test * test: fix tests * chore: fix formatting * test: refactor tests & add `TestDataGenerator` * fix: remove unnecessary null check * chore: fix domain error message * chore: fix accidentally removed blank line * chore: fix formatting * chore: rename variable * chore: rename method * chore: revert & remove code * fix: remove status check from PNS handle * chore: remove unused code * refactor: SyncRunsController * chore: undo unnecessary changes to this file * chore: cleanup * feat: block push notifications * refactor: block starting a sync run for identities in status `ToBeDeleted` * chore: fix formatting * test: improve & fix tests * test: add new test * fix: remove user context * refactor: split step definitions * test: add assignment to WhenResponse --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Timo Notheisen --- .../Features/Messages/POST.feature | 5 ++- .../Relationships/{id}/Accept/PUT.feature | 4 +-- .../Relationships/{id}/Reject/PUT.feature | 4 +-- .../Relationships/{id}/Revoke/PUT.feature | 6 ++-- .../RelationshipsStepDefinitions.cs | 31 +++++++++------- .../Tests/ActualDeletionWorkerTests.cs | 3 +- .../ExternalEventCreatedDomainEventHandler.cs | 12 +++++-- ...rnalEventCreatedDomainEventHandlerTests.cs | 36 +++++++++++++++++-- .../Messages.Application/ApplicationErrors.cs | 7 ---- .../Controllers/MessagesController.cs | 7 ---- .../Extensions/IEventBusExtensions.cs | 7 ++-- .../Controllers/RelationshipsController.cs | 25 ------------- .../ApplicationErrors.cs | 6 ++++ .../Extensions/IEventBusExtensions.cs | 4 +-- .../Controllers/SyncRunsController.cs | 17 +++++++-- 15 files changed, 98 insertions(+), 76 deletions(-) diff --git a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Messages/POST.feature b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Messages/POST.feature index 4695759f93..82c04534be 100644 --- a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Messages/POST.feature +++ b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Messages/POST.feature @@ -15,9 +15,8 @@ Identity sends a Message And an active Relationship r12 between i1 and i2 And i2 is in status "ToBeDeleted" When i1 sends a POST request to the /Messages endpoint with i2 as recipient - Then the response status code is 400 (Bad Request) - And the response content contains an error with the error code "error.platform.validation.message.recipientToBeDeleted" - And the error contains a list of Identities to be deleted that includes i2 + Then the response status code is 201 (Created) + And the response contains a SendMessageResponse Scenario: Sending a Message to a terminated Relationship Given Identities i1 and i2 diff --git a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Accept/PUT.feature b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Accept/PUT.feature index 4d4a198dae..6ad26bb04b 100644 --- a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Accept/PUT.feature +++ b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Accept/PUT.feature @@ -15,5 +15,5 @@ User accepts a Relationship And a pending Relationship r between i1 and i2 created by i2 And i2 is in status "ToBeDeleted" When i1 sends a PUT request to the /Relationships/{r.Id}/Accept endpoint - Then the response status code is 400 (Bad Request) - And the response content contains an error with the error code "error.platform.validation.relationship.peerIsToBeDeleted" + Then the response status code is 200 (OK) + And the response contains a RelationshipMetadata diff --git a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Reject/PUT.feature b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Reject/PUT.feature index e507afe429..ed5055ab99 100644 --- a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Reject/PUT.feature +++ b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Reject/PUT.feature @@ -15,5 +15,5 @@ User rejects a Relationship And a pending Relationship r between i1 and i2 created by i2 And i2 is in status "ToBeDeleted" When i1 sends a PUT request to the /Relationships/{r.Id}/Reject endpoint - Then the response status code is 400 (Bad Request) - And the response content contains an error with the error code "error.platform.validation.relationship.peerIsToBeDeleted" + Then the response status code is 200 (OK) + And the response contains a RelationshipMetadata diff --git a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Revoke/PUT.feature b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Revoke/PUT.feature index 805b05d68f..7e48bf039e 100644 --- a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Revoke/PUT.feature +++ b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/Features/Relationships/{id}/Revoke/PUT.feature @@ -12,8 +12,8 @@ User revokes a Relationship Scenario: Revoke Relationship to an Identity in status "ToBeDeleted" Given Identities i1 and i2 - And a pending Relationship r between i1 and i2 created by i2 + And a pending Relationship r between i1 and i2 created by i1 And i2 is in status "ToBeDeleted" When i1 sends a PUT request to the /Relationships/{r.Id}/Revoke endpoint - Then the response status code is 400 (Bad Request) - And the response content contains an error with the error code "error.platform.validation.relationship.peerIsToBeDeleted" + Then the response status code is 200 (OK) + And the response contains a RelationshipMetadata diff --git a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/StepDefinitions/RelationshipsStepDefinitions.cs b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/StepDefinitions/RelationshipsStepDefinitions.cs index e5a66839cb..8eaaf020d4 100644 --- a/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/StepDefinitions/RelationshipsStepDefinitions.cs +++ b/Applications/ConsumerApi/test/ConsumerApi.Tests.Integration/StepDefinitions/RelationshipsStepDefinitions.cs @@ -128,21 +128,28 @@ public async Task WhenIdentitySendsAPostRequestToTheRelationshipsEndpointWithRel await client.Relationships.CreateRelationship(new CreateRelationshipRequest { RelationshipTemplateId = relationshipTemplateId, Content = TestData.SOME_BYTES }); } - [When($"{RegexFor.SINGLE_THING} sends a PUT request to the /Relationships/{{{RegexFor.SINGLE_THING}.Id}}/(Accept|Reject|Revoke) endpoint")] - public async Task WhenIdentitySendsAPostRequestToTheRelationshipsIdEndpoint(string identityName, string relationshipName, string requestType) + [When($"{RegexFor.SINGLE_THING} sends a PUT request to the /Relationships/{{{RegexFor.SINGLE_THING}.Id}}/Accept endpoint")] + public async Task WhenIdentitySendsAPostRequestToTheRelationshipsIdAcceptEndpoint(string identityName, string relationshipName) { var client = _clientPool.FirstForIdentityName(identityName); + var relationship = _relationshipsContext.Relationships[relationshipName]; + _responseContext.WhenResponse = await client.Relationships.AcceptRelationship(relationship.Id, new AcceptRelationshipRequest { CreationResponseContent = TestData.SOME_BYTES }); + } - _responseContext.WhenResponse = requestType switch - { - "Accept" => await client.Relationships.AcceptRelationship(_relationshipsContext.Relationships[relationshipName].Id, - new AcceptRelationshipRequest { CreationResponseContent = TestData.SOME_BYTES }), - "Reject" => await client.Relationships.RejectRelationship(_relationshipsContext.Relationships[relationshipName].Id, - new RejectRelationshipRequest { CreationResponseContent = TestData.SOME_BYTES }), - "Revoke" => await client.Relationships.RevokeRelationship(_relationshipsContext.Relationships[relationshipName].Id, - new RevokeRelationshipRequest { CreationResponseContent = TestData.SOME_BYTES }), - _ => throw new NotSupportedException($"Unsupported request type: {requestType}") - }; + [When($"{RegexFor.SINGLE_THING} sends a PUT request to the /Relationships/{{{RegexFor.SINGLE_THING}.Id}}/Reject endpoint")] + public async Task WhenIdentitySendsAPostRequestToTheRelationshipsIdRejectEndpoint(string identityName, string relationshipName) + { + var client = _clientPool.FirstForIdentityName(identityName); + var relationship = _relationshipsContext.Relationships[relationshipName]; + _responseContext.WhenResponse = await client.Relationships.RejectRelationship(relationship.Id, new RejectRelationshipRequest { CreationResponseContent = TestData.SOME_BYTES }); + } + + [When($"{RegexFor.SINGLE_THING} sends a PUT request to the /Relationships/{{{RegexFor.SINGLE_THING}.Id}}/Revoke endpoint")] + public async Task WhenIdentitySendsAPostRequestToTheRelationshipsIdRevokeEndpoint(string identityName, string relationshipName) + { + var client = _clientPool.FirstForIdentityName(identityName); + var relationship = _relationshipsContext.Relationships[relationshipName]; + _responseContext.WhenResponse = await client.Relationships.RevokeRelationship(relationship.Id, new RevokeRelationshipRequest { CreationResponseContent = TestData.SOME_BYTES }); } [When($"{RegexFor.SINGLE_THING} sends a PUT request to the /Relationships/{{{RegexFor.SINGLE_THING}.Id}}/Terminate endpoint")] diff --git a/Applications/IdentityDeletionJobs/test/Job.IdentityDeletion.Tests/Tests/ActualDeletionWorkerTests.cs b/Applications/IdentityDeletionJobs/test/Job.IdentityDeletion.Tests/Tests/ActualDeletionWorkerTests.cs index 943c64b3f9..319bf33b8e 100644 --- a/Applications/IdentityDeletionJobs/test/Job.IdentityDeletion.Tests/Tests/ActualDeletionWorkerTests.cs +++ b/Applications/IdentityDeletionJobs/test/Job.IdentityDeletion.Tests/Tests/ActualDeletionWorkerTests.cs @@ -5,7 +5,6 @@ using Backbone.Job.IdentityDeletion.Workers; using Backbone.Modules.Devices.Application.Identities.Commands.TriggerRipeDeletionProcesses; using Backbone.Modules.Relationships.Application.Relationships.Queries.FindRelationshipsOfIdentity; -using Backbone.Modules.Relationships.Domain.Aggregates.Relationships; using CSharpFunctionalExtensions; using FakeItEasy; using MediatR; @@ -45,7 +44,7 @@ public async Task Calls_Deleters_For_Each_Identity() var worker = CreateWorker(fakeMediator, [mockIdentityDeleter]); A.CallTo(() => fakeMediator.Send(A._, A._)) - .Returns(new FindRelationshipsOfIdentityResponse(new List())); + .Returns(new FindRelationshipsOfIdentityResponse([])); // Act await worker.StartProcessing(CancellationToken.None); diff --git a/Modules/Devices/src/Devices.Application/DomainEvents/Incoming/ExternalEventCreated/ExternalEventCreatedDomainEventHandler.cs b/Modules/Devices/src/Devices.Application/DomainEvents/Incoming/ExternalEventCreated/ExternalEventCreatedDomainEventHandler.cs index 818c804c32..a1f87f6971 100644 --- a/Modules/Devices/src/Devices.Application/DomainEvents/Incoming/ExternalEventCreated/ExternalEventCreatedDomainEventHandler.cs +++ b/Modules/Devices/src/Devices.Application/DomainEvents/Incoming/ExternalEventCreated/ExternalEventCreatedDomainEventHandler.cs @@ -1,17 +1,22 @@ +using Backbone.BuildingBlocks.Application.Abstractions.Exceptions; using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.EventBus; using Backbone.BuildingBlocks.Application.PushNotifications; +using Backbone.Modules.Devices.Application.Infrastructure.Persistence.Repository; using Backbone.Modules.Devices.Application.Infrastructure.PushNotifications.ExternalEvents; using Backbone.Modules.Devices.Domain.DomainEvents.Incoming.ExternalEventCreated; +using Backbone.Modules.Devices.Domain.Entities.Identities; namespace Backbone.Modules.Devices.Application.DomainEvents.Incoming.ExternalEventCreated; public class ExternalEventCreatedDomainEventHandler : IDomainEventHandler { private readonly IPushNotificationSender _pushSenderService; + private readonly IIdentitiesRepository _identitiesRepository; - public ExternalEventCreatedDomainEventHandler(IPushNotificationSender pushSenderService) + public ExternalEventCreatedDomainEventHandler(IPushNotificationSender pushSenderService, IIdentitiesRepository identitiesRepository) { _pushSenderService = pushSenderService; + _identitiesRepository = identitiesRepository; } public async Task Handle(ExternalEventCreatedDomainEvent @event) @@ -19,6 +24,9 @@ public async Task Handle(ExternalEventCreatedDomainEvent @event) if (@event.IsDeliveryBlocked) return; - await _pushSenderService.SendNotification(@event.Owner, new ExternalEventCreatedPushNotification(), CancellationToken.None); + var identity = await _identitiesRepository.FindByAddress(@event.Owner, CancellationToken.None) ?? throw new NotFoundException(nameof(Identity)); + + if (identity.Status != IdentityStatus.ToBeDeleted) + await _pushSenderService.SendNotification(@event.Owner, new ExternalEventCreatedPushNotification(), CancellationToken.None); } } diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/DomainEvents/Incoming/ExternalEventCreatedDomainEventHandlerTests.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/DomainEvents/Incoming/ExternalEventCreatedDomainEventHandlerTests.cs index 5640906349..ad15b7081c 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/DomainEvents/Incoming/ExternalEventCreatedDomainEventHandlerTests.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/DomainEvents/Incoming/ExternalEventCreatedDomainEventHandlerTests.cs @@ -1,5 +1,6 @@ using Backbone.BuildingBlocks.Application.PushNotifications; using Backbone.Modules.Devices.Application.DomainEvents.Incoming.ExternalEventCreated; +using Backbone.Modules.Devices.Application.Infrastructure.Persistence.Repository; using Backbone.Modules.Devices.Application.Infrastructure.PushNotifications.ExternalEvents; using Backbone.Modules.Devices.Domain.DomainEvents.Incoming.ExternalEventCreated; using FakeItEasy; @@ -13,11 +14,15 @@ public async Task Sends_a_push_notification_to_the_owner_of_the_external_event() { // Arrange var mockPushSender = A.Fake(); + var fakeIdentitiesRepository = A.Fake(); + var identity = TestDataGenerator.CreateIdentity(); - var handler = new ExternalEventCreatedDomainEventHandler(mockPushSender); + var handler = new ExternalEventCreatedDomainEventHandler(mockPushSender, fakeIdentitiesRepository); var externalEventOwner = CreateRandomIdentityAddress(); + A.CallTo(() => fakeIdentitiesRepository.FindByAddress(externalEventOwner, A._, A._)).Returns(identity); + // Act await handler.Handle(new ExternalEventCreatedDomainEvent { Owner = externalEventOwner, IsDeliveryBlocked = false }); @@ -25,16 +30,43 @@ public async Task Sends_a_push_notification_to_the_owner_of_the_external_event() A.CallTo(() => mockPushSender.SendNotification(externalEventOwner, A._, A._)).MustHaveHappenedOnceExactly(); } + [Fact] + public async Task Does_not_send_push_notification_if_owner_is_in_status_to_be_deleted() + { + // Arrange + var mockPushSender = A.Fake(); + var fakeIdentitiesRepository = A.Fake(); + var identity = TestDataGenerator.CreateIdentity(); + + var handler = new ExternalEventCreatedDomainEventHandler(mockPushSender, fakeIdentitiesRepository); + + var externalEventOwner = CreateRandomIdentityAddress(); + + identity.StartDeletionProcessAsOwner(identity.Devices[0].Id); + + A.CallTo(() => fakeIdentitiesRepository.FindByAddress(externalEventOwner, A._, A._)).Returns(identity); + + // Act + await handler.Handle(new ExternalEventCreatedDomainEvent { Owner = externalEventOwner, IsDeliveryBlocked = false }); + + // Assert + A.CallTo(() => mockPushSender.SendNotification(externalEventOwner, A._, A._)).MustNotHaveHappened(); + } + [Fact] public async Task Does_not_send_a_push_notification_when_delivery_of_the_external_event_is_blocked() { // Arrange var mockPushSender = A.Fake(); + var fakeIdentitiesRepository = A.Fake(); + var identity = TestDataGenerator.CreateIdentity(); - var handler = new ExternalEventCreatedDomainEventHandler(mockPushSender); + var handler = new ExternalEventCreatedDomainEventHandler(mockPushSender, fakeIdentitiesRepository); var externalEventOwner = CreateRandomIdentityAddress(); + A.CallTo(() => fakeIdentitiesRepository.FindByAddress(externalEventOwner, A._, A._)).Returns(identity); + // Act await handler.Handle(new ExternalEventCreatedDomainEvent { Owner = externalEventOwner, IsDeliveryBlocked = true }); diff --git a/Modules/Messages/src/Messages.Application/ApplicationErrors.cs b/Modules/Messages/src/Messages.Application/ApplicationErrors.cs index 8436b71406..e5f241185a 100644 --- a/Modules/Messages/src/Messages.Application/ApplicationErrors.cs +++ b/Modules/Messages/src/Messages.Application/ApplicationErrors.cs @@ -4,13 +4,6 @@ namespace Backbone.Modules.Messages.Application; public static class ApplicationErrors { - public static ApplicationError RecipientsToBeDeleted(IEnumerable peersToBeDeleted) - { - return new ApplicationError("error.platform.validation.message.recipientToBeDeleted", - $"Cannot send message to {peersToBeDeleted.Count()} of the recipients because they are in status 'ToBeDeleted'.", - new { PeersToBeDeleted = peersToBeDeleted }); - } - public static ApplicationError NoRelationshipToRecipientExists(string recipient = "") { var recipientText = string.IsNullOrEmpty(recipient) ? "one of the recipients" : recipient; diff --git a/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs b/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs index 5cf43e9811..ce13403334 100644 --- a/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs +++ b/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs @@ -3,8 +3,6 @@ using Backbone.BuildingBlocks.API.Mvc.ControllerAttributes; using Backbone.BuildingBlocks.Application.Abstractions.Exceptions; using Backbone.BuildingBlocks.Application.Pagination; -using Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; -using Backbone.Modules.Devices.Domain.Entities.Identities; using Backbone.Modules.Messages.Application; using Backbone.Modules.Messages.Application.Messages.Commands.SendMessage; using Backbone.Modules.Messages.Application.Messages.DTOs; @@ -61,11 +59,6 @@ public async Task GetMessage(string id, [FromQuery] bool? noBody, [ProducesError(StatusCodes.Status400BadRequest)] public async Task SendMessage(SendMessageCommand request, CancellationToken cancellationToken) { - var recipientAddresses = request.Recipients.Select(r => r.Address).ToList(); - var identitiesToBeDeleted = await _mediator.Send(new ListIdentitiesQuery(recipientAddresses, IdentityStatus.ToBeDeleted), cancellationToken); - if (identitiesToBeDeleted.Any()) - throw new ApplicationException(ApplicationErrors.RecipientsToBeDeleted(identitiesToBeDeleted.Select(i => i.Address))); - var response = await _mediator.Send(request, cancellationToken); return CreatedAtAction(nameof(GetMessage), new { id = response.Id }, response); } diff --git a/Modules/Relationships/src/Relationships.Application/Extensions/IEventBusExtensions.cs b/Modules/Relationships/src/Relationships.Application/Extensions/IEventBusExtensions.cs index 8c66b94c0a..1949320c73 100644 --- a/Modules/Relationships/src/Relationships.Application/Extensions/IEventBusExtensions.cs +++ b/Modules/Relationships/src/Relationships.Application/Extensions/IEventBusExtensions.cs @@ -8,13 +8,12 @@ namespace Backbone.Modules.Relationships.Application.Extensions; public static class IEventBusExtensions { - public static IEventBus AddRelationshipsDomainEventSubscriptions(this IEventBus eventBus) + public static void AddRelationshipsDomainEventSubscriptions(this IEventBus eventBus) { - eventBus.SubscribeToIdentitiesEvents(); - return eventBus; + SubscribeToIdentitiesEvents(eventBus); } - private static void SubscribeToIdentitiesEvents(this IEventBus eventBus) + private static void SubscribeToIdentitiesEvents(IEventBus eventBus) { eventBus.Subscribe(); eventBus.Subscribe(); diff --git a/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs b/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs index ec5ec448c0..8af74b0655 100644 --- a/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs +++ b/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs @@ -18,7 +18,6 @@ using Backbone.Modules.Relationships.Application.Relationships.Commands.TerminateRelationship; using Backbone.Modules.Relationships.Application.Relationships.DTOs; using Backbone.Modules.Relationships.Application.Relationships.Queries.CanEstablishRelationship; -using Backbone.Modules.Relationships.Application.Relationships.Queries.GetPeerOfActiveIdentityInRelationship; using Backbone.Modules.Relationships.Application.Relationships.Queries.GetRelationship; using Backbone.Modules.Relationships.Application.Relationships.Queries.ListRelationships; using Backbone.Modules.Relationships.Application.RelationshipTemplates.Queries.GetRelationshipTemplate; @@ -87,9 +86,6 @@ public async Task CreateRelationship(CreateRelationshipCommand re [ProducesError(StatusCodes.Status404NotFound)] public async Task AcceptRelationship([FromRoute] string id, [FromBody] AcceptRelationshipRequest request, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var response = await _mediator.Send(new AcceptRelationshipCommand { RelationshipId = id, CreationResponseContent = request.CreationResponseContent }, cancellationToken); return Ok(response); } @@ -100,9 +96,6 @@ public async Task AcceptRelationship([FromRoute] string id, [From [ProducesError(StatusCodes.Status404NotFound)] public async Task RejectRelationship([FromRoute] string id, [FromBody] RejectRelationshipRequest request, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var response = await _mediator.Send(new RejectRelationshipCommand { RelationshipId = id, CreationResponseContent = request.CreationResponseContent }, cancellationToken); return Ok(response); } @@ -113,9 +106,6 @@ public async Task RejectRelationship([FromRoute] string id, [From [ProducesError(StatusCodes.Status404NotFound)] public async Task RevokeRelationship([FromRoute] string id, [FromBody] RevokeRelationshipRequest request, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var response = await _mediator.Send(new RevokeRelationshipCommand { RelationshipId = id, CreationResponseContent = request.CreationResponseContent }, cancellationToken); return Ok(response); } @@ -126,9 +116,6 @@ public async Task RevokeRelationship([FromRoute] string id, [From [ProducesError(StatusCodes.Status404NotFound)] public async Task TerminateRelationship([FromRoute] string id, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var relationship = await _mediator.Send(new TerminateRelationshipCommand { RelationshipId = id }, cancellationToken); return Ok(relationship); } @@ -139,9 +126,6 @@ public async Task TerminateRelationship([FromRoute] string id, Ca [ProducesError(StatusCodes.Status404NotFound)] public async Task RelationshipReactivationRequest([FromRoute] string id, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var response = await _mediator.Send(new RequestRelationshipReactivationCommand { RelationshipId = id }, cancellationToken); return Ok(response); } @@ -152,9 +136,6 @@ public async Task RelationshipReactivationRequest([FromRoute] str [ProducesError(StatusCodes.Status404NotFound)] public async Task RevokeRelationshipReactivation([FromRoute] string id, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var response = await _mediator.Send(new RevokeRelationshipReactivationCommand { RelationshipId = id }, cancellationToken); return Ok(response); } @@ -165,9 +146,6 @@ public async Task RevokeRelationshipReactivation([FromRoute] stri [ProducesError(StatusCodes.Status404NotFound)] public async Task AcceptReactivationOfRelationship([FromRoute] string id, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var response = await _mediator.Send(new AcceptRelationshipReactivationCommand { RelationshipId = id }, cancellationToken); return Ok(response); } @@ -178,9 +156,6 @@ public async Task AcceptReactivationOfRelationship([FromRoute] st [ProducesError(StatusCodes.Status404NotFound)] public async Task RejectReactivationOfRelationship([FromRoute] string id, CancellationToken cancellationToken) { - var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = id }, cancellationToken); - await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); - var response = await _mediator.Send(new RejectRelationshipReactivationCommand { RelationshipId = id }, cancellationToken); return Ok(response); } diff --git a/Modules/Synchronization/src/Synchronization.Application/ApplicationErrors.cs b/Modules/Synchronization/src/Synchronization.Application/ApplicationErrors.cs index 0e6b094a30..6582fb8066 100644 --- a/Modules/Synchronization/src/Synchronization.Application/ApplicationErrors.cs +++ b/Modules/Synchronization/src/Synchronization.Application/ApplicationErrors.cs @@ -65,5 +65,11 @@ public static ApplicationError UnexpectedSyncRunType(SyncRun.SyncRunType expecte { return new ApplicationError("error.platform.validation.syncRun.unexpectedSyncRunType", $"The current operation only supports sync runs of type '{expectedType}'."); } + + public static ApplicationError CannotStartSyncRunWhileIdentityIsToBeDeleted() + { + return new ApplicationError("error.platform.validation.syncRun.cannotStartSyncRunWhileIdentityIsToBeDeleted", + "Cannot start sync run because the identity is in status 'ToBeDeleted'."); + } } } diff --git a/Modules/Synchronization/src/Synchronization.Application/Extensions/IEventBusExtensions.cs b/Modules/Synchronization/src/Synchronization.Application/Extensions/IEventBusExtensions.cs index 4c374ac8e6..0ce6264bca 100644 --- a/Modules/Synchronization/src/Synchronization.Application/Extensions/IEventBusExtensions.cs +++ b/Modules/Synchronization/src/Synchronization.Application/Extensions/IEventBusExtensions.cs @@ -22,12 +22,10 @@ namespace Backbone.Modules.Synchronization.Application.Extensions; public static class IEventBusExtensions { - public static IEventBus AddSynchronizationDomainEventSubscriptions(this IEventBus eventBus) + public static void AddSynchronizationDomainEventSubscriptions(this IEventBus eventBus) { SubscribeToMessagesEvents(eventBus); SubscribeToRelationshipsEvents(eventBus); - - return eventBus; } private static void SubscribeToMessagesEvents(IEventBus eventBus) diff --git a/Modules/Synchronization/src/Synchronization.ConsumerApi/Controllers/SyncRunsController.cs b/Modules/Synchronization/src/Synchronization.ConsumerApi/Controllers/SyncRunsController.cs index 16273d074a..6b348d5d99 100644 --- a/Modules/Synchronization/src/Synchronization.ConsumerApi/Controllers/SyncRunsController.cs +++ b/Modules/Synchronization/src/Synchronization.ConsumerApi/Controllers/SyncRunsController.cs @@ -2,7 +2,10 @@ using Backbone.BuildingBlocks.API.Mvc; using Backbone.BuildingBlocks.API.Mvc.ControllerAttributes; using Backbone.BuildingBlocks.Application.Abstractions.Exceptions; +using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.UserContext; using Backbone.BuildingBlocks.Application.Pagination; +using Backbone.Modules.Devices.Application.Identities.Queries.GetIdentity; +using Backbone.Modules.Devices.Domain.Entities.Identities; using Backbone.Modules.Synchronization.Application; using Backbone.Modules.Synchronization.Application.Datawallets.DTOs; using Backbone.Modules.Synchronization.Application.SyncRuns.Commands.FinalizeSyncRun; @@ -26,13 +29,14 @@ namespace Backbone.Modules.Synchronization.ConsumerApi.Controllers; public class SyncRunsController : ApiControllerBase { private readonly ApplicationOptions _options; + private readonly IUserContext _userContext; - public SyncRunsController(IMediator mediator, IOptions options) : base(mediator) + public SyncRunsController(IMediator mediator, IOptions options, IUserContext userContext) : base(mediator) { _options = options.Value; + _userContext = userContext; } - [HttpPost] [ProducesResponseType(typeof(HttpResponseEnvelopeResult), StatusCodes.Status200OK)] [ProducesError(StatusCodes.Status400BadRequest)] @@ -40,6 +44,9 @@ public async Task StartSyncRun(StartSyncRunRequestBody requestBod [FromHeader(Name = "X-Supported-Datawallet-Version")] ushort supportedDatawalletVersion, CancellationToken cancellationToken) { + var identityResponse = await _mediator.Send(new GetIdentityQuery(_userContext.GetAddress()), cancellationToken); + EnsureIdentityIsNotToBeDeleted(identityResponse); + var response = await _mediator.Send(new StartSyncRunCommand( requestBody.Type ?? SyncRunDTO.SyncRunType.ExternalEventSync, requestBody.Duration, supportedDatawalletVersion), cancellationToken); @@ -112,6 +119,12 @@ public async Task RefreshExpirationTimeOfSyncRun(SyncRunId id, Ca var response = await _mediator.Send(new RefreshExpirationTimeOfSyncRunCommand(id), cancellationToken); return Ok(response); } + + private static void EnsureIdentityIsNotToBeDeleted(GetIdentityResponse identityResponse) + { + if (identityResponse.Status is IdentityStatus.ToBeDeleted) + throw new ApplicationException(ApplicationErrors.SyncRuns.CannotStartSyncRunWhileIdentityIsToBeDeleted()); + } } public class StartSyncRunRequestBody