From f47fad4675df6cb59538a9c30b3248797d264523 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:18:08 +0100 Subject: [PATCH 01/12] feat: add find all method with possible identity address collection filter --- .../Devices.Application/DTOs/IdentitySummaryDTO.cs | 2 ++ .../Identities/Queries/ListIdentities/Handler.cs | 7 ++++--- .../Queries/ListIdentities/ListIdentitiesQuery.cs | 10 ++++++---- .../ListIdentities/ListIdentitiesResponse.cs | 9 ++++++--- .../Persistence/Repository/IIdentitiesRepository.cs | 3 ++- .../Persistence/Repository/IdentitiesRepository.cs | 13 ++++++++++++- 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/Modules/Devices/src/Devices.Application/DTOs/IdentitySummaryDTO.cs b/Modules/Devices/src/Devices.Application/DTOs/IdentitySummaryDTO.cs index b28744e4f2..2354585c44 100644 --- a/Modules/Devices/src/Devices.Application/DTOs/IdentitySummaryDTO.cs +++ b/Modules/Devices/src/Devices.Application/DTOs/IdentitySummaryDTO.cs @@ -12,6 +12,7 @@ public IdentitySummaryDTO(Identity identity) Address = identity.Address.ToString(); PublicKey = identity.PublicKey; CreatedAt = identity.CreatedAt; + Status = identity.Status; Devices = identity.Devices.Select(d => new DeviceDTO { @@ -33,6 +34,7 @@ public IdentitySummaryDTO(Identity identity) public string? ClientId { get; set; } public byte[] PublicKey { get; set; } public DateTime CreatedAt { get; set; } + public IdentityStatus Status { get; set; } public IEnumerable Devices { get; set; } public int NumberOfDevices { get; set; } diff --git a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs index 19bdda8145..80a362fb01 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs @@ -3,6 +3,7 @@ using MediatR; namespace Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; + public class Handler : IRequestHandler { private readonly IIdentitiesRepository _identitiesRepository; @@ -14,9 +15,9 @@ public Handler(IIdentitiesRepository repository) public async Task Handle(ListIdentitiesQuery request, CancellationToken cancellationToken) { - var dbPaginationResult = await _identitiesRepository.FindAll(request.PaginationFilter, cancellationToken); - var identityDtos = dbPaginationResult.ItemsOnPage.Select(el => new IdentitySummaryDTO(el)).ToList(); + var identities = await _identitiesRepository.FindAllWithAddresses(request.Addresses, cancellationToken); + var identityDtos = identities.Select(el => new IdentitySummaryDTO(el)).ToList(); - return new ListIdentitiesResponse(identityDtos, request.PaginationFilter, dbPaginationResult.TotalNumberOfItems); + return new ListIdentitiesResponse(identityDtos); } } diff --git a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs index 9b2c2d3c35..deac227b8d 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs @@ -1,12 +1,14 @@ -using Backbone.BuildingBlocks.Application.Pagination; +using Backbone.DevelopmentKit.Identity.ValueObjects; using MediatR; namespace Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; + public class ListIdentitiesQuery : IRequest { - public ListIdentitiesQuery(PaginationFilter paginationFilter) + public ListIdentitiesQuery(IEnumerable addresses) { - PaginationFilter = paginationFilter; + Addresses = addresses; } - public PaginationFilter PaginationFilter { get; set; } + + public IEnumerable Addresses { get; set; } } diff --git a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesResponse.cs b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesResponse.cs index 7352959245..3cbe69e6c5 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesResponse.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesResponse.cs @@ -1,8 +1,11 @@ -using Backbone.BuildingBlocks.Application.Pagination; +using Backbone.BuildingBlocks.Application.CQRS.BaseClasses; using Backbone.Modules.Devices.Application.DTOs; namespace Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; -public class ListIdentitiesResponse : PagedResponse + +public class ListIdentitiesResponse : CollectionResponseBase { - public ListIdentitiesResponse(IEnumerable items, PaginationFilter previousPaginationFilter, int totalRecords) : base(items, previousPaginationFilter, totalRecords) { } + public ListIdentitiesResponse(IEnumerable items) : base(items) + { + } } diff --git a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs index d93f21d058..c9cd6109d6 100644 --- a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs @@ -9,10 +9,11 @@ namespace Backbone.Modules.Devices.Application.Infrastructure.Persistence.Reposi public interface IIdentitiesRepository { #region Identities - Task> FindAll(PaginationFilter paginationFilter, CancellationToken cancellationToken); + Task Update(Identity identity, CancellationToken cancellationToken); Task FindByAddress(IdentityAddress address, CancellationToken cancellationToken, bool track = false); Task Exists(IdentityAddress address, CancellationToken cancellationToken); + Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false); Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false); Task CountByClientId(string clientId, CancellationToken cancellationToken); Task> Find(Expression> filter, CancellationToken cancellationToken, bool track = false); diff --git a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs index 2319f8439b..620f3eb601 100644 --- a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs @@ -9,7 +9,6 @@ using Backbone.Modules.Devices.Domain.Entities.Identities; using Backbone.Modules.Devices.Infrastructure.Persistence.Database; using Backbone.Modules.Devices.Infrastructure.Persistence.Database.QueryableExtensions; -using Backbone.Tooling; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; @@ -55,6 +54,18 @@ public async Task Exists(IdentityAddress address, CancellationToken cancel .AnyAsync(i => i.Address == address, cancellationToken); } + public async Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false) + { + var query = (track ? _identities : _readonlyIdentities) + .IncludeAll(_dbContext); + + if (addresses.Any()) + query = query.Where(i => addresses.Contains(i.Address)); + + return await query + .ToListAsync(cancellationToken); + } + public async Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) { return await (track ? _identities : _readonlyIdentities) From c6988f04aa3723c505ee64b070fb9de31e3bc20f Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:18:25 +0100 Subject: [PATCH 02/12] test: unit test find all identities handler --- .../FindByAddressStubRepository.cs | 13 ++-- ...Repository.cs => FindAllFakeRepository.cs} | 20 +++--- .../Queries/ListIdentities/HandlerTests.cs | 71 +++++++++++-------- 3 files changed, 59 insertions(+), 45 deletions(-) rename Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/{FindAllStubRepository.cs => FindAllFakeRepository.cs} (84%) diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs index 3b20f17cd9..a5f743fb36 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs @@ -21,27 +21,22 @@ public Task Exists(IdentityAddress address, CancellationToken cancellation throw new NotImplementedException(); } - public Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) - { - throw new NotImplementedException(); - } - - public Task CountByClientId(string clientId, CancellationToken cancellationToken) + public Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false) { throw new NotImplementedException(); } - public Task AddUser(ApplicationUser user, string password) + public Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) { throw new NotImplementedException(); } - public Task> FindAll(PaginationFilter paginationFilter, CancellationToken cancellationToken) + public Task CountByClientId(string clientId, CancellationToken cancellationToken) { throw new NotImplementedException(); } - public Task> FindAllWithDeletionProcessWaitingForApproval(CancellationToken cancellationToken, bool track = false) + public Task AddUser(ApplicationUser user, string password) { throw new NotImplementedException(); } diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllFakeRepository.cs similarity index 84% rename from Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs rename to Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllFakeRepository.cs index 91a0be8c90..f81fc79fe4 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllFakeRepository.cs @@ -7,11 +7,11 @@ namespace Backbone.Modules.Devices.Application.Tests.Tests.Identities.Queries.ListIdentities; -public class FindAllStubRepository : IIdentitiesRepository +public class FindAllFakeRepository : IIdentitiesRepository { - private readonly DbPaginationResult _identities; + private readonly IEnumerable _identities; - public FindAllStubRepository(DbPaginationResult identities) + public FindAllFakeRepository(IEnumerable identities) { _identities = identities; } @@ -21,6 +21,15 @@ public Task Exists(IdentityAddress address, CancellationToken cancellation throw new NotImplementedException(); } + public Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false) + { + var result = _identities; + if (addresses.Any()) + result = result.Where(i => addresses.Contains(i.Address)); + + return Task.FromResult(result); + } + public Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) { throw new NotImplementedException(); @@ -36,11 +45,6 @@ public Task AddUser(ApplicationUser user, string password) throw new NotImplementedException(); } - public Task> FindAll(PaginationFilter paginationFilter, CancellationToken cancellationToken) - { - return Task.FromResult(_identities); - } - public Task> FindAllWithDeletionProcessWaitingForApproval(CancellationToken cancellationToken, bool track = false) { throw new NotImplementedException(); diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs index 836714f08f..bffae03182 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs @@ -1,10 +1,8 @@ -using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.Persistence.Database; -using Backbone.BuildingBlocks.Application.Pagination; +using Backbone.DevelopmentKit.Identity.ValueObjects; using Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; using Backbone.Modules.Devices.Domain.Entities.Identities; using Backbone.UnitTestTools.BaseClasses; using FluentAssertions; -using FluentAssertions.Execution; using Xunit; using static Backbone.UnitTestTools.Data.TestDataGenerator; @@ -12,31 +10,24 @@ namespace Backbone.Modules.Devices.Application.Tests.Tests.Identities.Queries.Li public class HandlerTests : AbstractTestsBase { - public HandlerTests() - { - AssertionScope.Current.FormattingOptions.MaxLines = 1000; - } - [Fact] public async Task Returns_an_empty_list_when_no_identities_exist() { // Arrange var identitiesList = new List(); - var request = new PaginationFilter() { PageSize = 5 }; - var handler = CreateHandler(new FindAllStubRepository(MakeDbPaginationResult(identitiesList))); + var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); // Act - var result = await handler.Handle(new ListIdentitiesQuery(request), CancellationToken.None); + var result = await handler.Handle(new ListIdentitiesQuery(new List()), CancellationToken.None); // Assert - result.Should().HaveCount(0); + result.Should().HaveCount(identitiesList.Count); } [Fact] - public async Task Returns_a_list_of_all_existing_identities() + public async Task Returns_a_list_of_all_identities_when_addresses_list_is_empty() { // Arrange - var request = new PaginationFilter(); List identitiesList = [ new Identity(CreateRandomDeviceId(), @@ -51,30 +42,58 @@ public async Task Returns_a_list_of_all_existing_identities() TestDataGenerator.CreateRandomTierId(), 1) ]; + var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); + + // Act + var result = await handler.Handle(new ListIdentitiesQuery(new List()), CancellationToken.None); + + // Assert + result.Should().HaveCount(identitiesList.Count); + } + + + [Fact] + public async Task Returns_a_list_of_identities_with_matching_address_from_addresses_list_when_not_empty() + { + // Arrange + var firstIdentityAddress = CreateRandomIdentityAddress(); + List identitiesList = + [ + new Identity(CreateRandomDeviceId(), + firstIdentityAddress, + CreateRandomBytes(), + TestDataGenerator.CreateRandomTierId(), + 1), - var handler = CreateHandler(new FindAllStubRepository(MakeDbPaginationResult(identitiesList))); + new Identity(CreateRandomDeviceId(), + CreateRandomIdentityAddress(), + CreateRandomBytes(), + TestDataGenerator.CreateRandomTierId(), + 1) + ]; + var addresses = new List { firstIdentityAddress }; + var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); // Act - var result = await handler.Handle(new ListIdentitiesQuery(request), CancellationToken.None); + var result = await handler.Handle(new ListIdentitiesQuery(addresses), CancellationToken.None); // Assert - result.Should().HaveCount(2); + result.Should().HaveCount(addresses.Count); } [Fact] public async Task Returned_identities_have_all_properties_filled_as_expected() { // Arrange - var request = new PaginationFilter(); var expectedClientId = CreateRandomDeviceId(); var expectedAddress = CreateRandomIdentityAddress(); var expectedTierId = TestDataGenerator.CreateRandomTierId(); - List identitiesList = [new(expectedClientId, expectedAddress, [], expectedTierId, 1)]; + List identitiesList = [new Identity(expectedClientId, expectedAddress, [], expectedTierId, 1)]; - var handler = CreateHandler(new FindAllStubRepository(MakeDbPaginationResult(identitiesList))); + var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); // Act - var result = await handler.Handle(new ListIdentitiesQuery(request), CancellationToken.None); + var result = await handler.Handle(new ListIdentitiesQuery(new List()), CancellationToken.None); // Assert result.Should().HaveCount(1); @@ -82,16 +101,12 @@ public async Task Returned_identities_have_all_properties_filled_as_expected() result.First().Address.Should().Be(expectedAddress); result.First().PublicKey.Should().BeEquivalentTo(Array.Empty()); result.First().TierId.Should().BeEquivalentTo(expectedTierId); + result.First().Status.Should().Be(IdentityStatus.Active); result.First().IdentityVersion.Should().Be(1); } - private Handler CreateHandler(FindAllStubRepository findAllStubRepository) - { - return new Handler(findAllStubRepository); - } - - private DbPaginationResult MakeDbPaginationResult(List identities) + private Handler CreateHandler(FindAllFakeRepository findAllFakeRepository) { - return new DbPaginationResult(identities, identities.Count); + return new Handler(findAllFakeRepository); } } From 70c444b18e48da9567f08fa3a88c1252f8e10c08 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:19:08 +0100 Subject: [PATCH 03/12] feat: ensure message is only sent if none of the recipients has an identity to be deleted --- .../Mvc/ExceptionFilters/CustomExceptionFilter.cs | 2 +- .../Exceptions/ApplicationError.cs | 4 +++- .../Exceptions/ApplicationException.cs | 3 +++ .../src/Messages.Application/ApplicationErrors.cs | 7 +++++++ .../Controllers/MessagesController.cs | 8 ++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/BuildingBlocks/src/BuildingBlocks.API/Mvc/ExceptionFilters/CustomExceptionFilter.cs b/BuildingBlocks/src/BuildingBlocks.API/Mvc/ExceptionFilters/CustomExceptionFilter.cs index e7e80b1f99..12895b6b71 100644 --- a/BuildingBlocks/src/BuildingBlocks.API/Mvc/ExceptionFilters/CustomExceptionFilter.cs +++ b/BuildingBlocks/src/BuildingBlocks.API/Mvc/ExceptionFilters/CustomExceptionFilter.cs @@ -143,7 +143,7 @@ private HttpError CreateHttpErrorForDomainException(DomainException domainExcept }); } - return null; + return applicationException.AdditionalData; } private static HttpStatusCode GetStatusCodeForInfrastructureException(InfrastructureException _) diff --git a/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationError.cs b/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationError.cs index 074bb43cb8..66a1ecd14f 100644 --- a/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationError.cs +++ b/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationError.cs @@ -2,12 +2,14 @@ namespace Backbone.BuildingBlocks.Application.Abstractions.Exceptions; public class ApplicationError { - public ApplicationError(string code, string message) + public ApplicationError(string code, string message, dynamic? additionalData = null) { Code = code; Message = message; + AdditionalData = additionalData; } public string Code { get; } public string Message { get; } + public dynamic? AdditionalData { get; } } diff --git a/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationException.cs b/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationException.cs index 96bcabce4d..01501576a9 100644 --- a/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationException.cs +++ b/BuildingBlocks/src/BuildingBlocks.Application.Abstractions/Exceptions/ApplicationException.cs @@ -5,13 +5,16 @@ public class ApplicationException : Exception public ApplicationException(ApplicationError error) : base(error.Message) { Code = error.Code; + AdditionalData = error.AdditionalData; } public ApplicationException(ApplicationError error, Exception innerException) : base(error.Message, innerException) { Code = error.Code; + AdditionalData = error.AdditionalData; } public string Code { get; set; } + public dynamic? AdditionalData { get; } } diff --git a/Modules/Messages/src/Messages.Application/ApplicationErrors.cs b/Modules/Messages/src/Messages.Application/ApplicationErrors.cs index 9a0ca52e26..45362d7190 100644 --- a/Modules/Messages/src/Messages.Application/ApplicationErrors.cs +++ b/Modules/Messages/src/Messages.Application/ApplicationErrors.cs @@ -4,6 +4,13 @@ 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 their identity is set to be deleted.", + 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 d3aa296a81..e771b579c4 100644 --- a/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs +++ b/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs @@ -3,6 +3,8 @@ 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; @@ -60,6 +62,12 @@ public async Task GetMessage(MessageId id, [FromQuery] bool? noBo [ProducesError(StatusCodes.Status400BadRequest)] public async Task SendMessage(SendMessageCommand request, CancellationToken cancellationToken) { + var recipientAddresses = request.Recipients.Select(r => r.Address); + var recipientIdentities = await _mediator.Send(new ListIdentitiesQuery(recipientAddresses), cancellationToken); + var identitiesToBeDeleted = recipientIdentities.Where(i => i.Status == IdentityStatus.ToBeDeleted).ToList(); + 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); } From c9206d22674f8feee3fdce3d716d00edc6b25344 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:29:14 +0100 Subject: [PATCH 04/12] refactor: add status filter to list identities query to prevent pulling too many instances into memory --- .../Queries/ListIdentities/Handler.cs | 2 +- .../ListIdentities/ListIdentitiesQuery.cs | 7 ++- .../Repository/IIdentitiesRepository.cs | 2 +- .../Repository/IdentitiesRepository.cs | 13 ++--- .../FindByAddressStubRepository.cs | 2 +- ...Repository.cs => FindAllStubRepository.cs} | 12 ++--- .../Queries/ListIdentities/HandlerTests.cs | 49 ++++--------------- .../Controllers/MessagesController.cs | 3 +- 8 files changed, 26 insertions(+), 64 deletions(-) rename Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/{FindAllFakeRepository.cs => FindAllStubRepository.cs} (86%) diff --git a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs index 80a362fb01..c11339e432 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs @@ -15,7 +15,7 @@ public Handler(IIdentitiesRepository repository) public async Task Handle(ListIdentitiesQuery request, CancellationToken cancellationToken) { - var identities = await _identitiesRepository.FindAllWithAddresses(request.Addresses, cancellationToken); + var identities = await _identitiesRepository.FindAllWithAddresses(cancellationToken, request.Addresses, request.Status); var identityDtos = identities.Select(el => new IdentitySummaryDTO(el)).ToList(); return new ListIdentitiesResponse(identityDtos); diff --git a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs index deac227b8d..0c66a83832 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/ListIdentitiesQuery.cs @@ -1,14 +1,17 @@ using Backbone.DevelopmentKit.Identity.ValueObjects; +using Backbone.Modules.Devices.Domain.Entities.Identities; using MediatR; namespace Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; public class ListIdentitiesQuery : IRequest { - public ListIdentitiesQuery(IEnumerable addresses) + public ListIdentitiesQuery(IEnumerable? addresses = null, IdentityStatus? status = null) { Addresses = addresses; + Status = status; } - public IEnumerable Addresses { get; set; } + public IEnumerable? Addresses { get; set; } + public IdentityStatus? Status { get; set; } } diff --git a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs index c9cd6109d6..7470d106c4 100644 --- a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs @@ -13,7 +13,7 @@ public interface IIdentitiesRepository Task Update(Identity identity, CancellationToken cancellationToken); Task FindByAddress(IdentityAddress address, CancellationToken cancellationToken, bool track = false); Task Exists(IdentityAddress address, CancellationToken cancellationToken); - Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false); + Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses = null, IdentityStatus? requestStatus = null, bool track = false); Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false); Task CountByClientId(string clientId, CancellationToken cancellationToken); Task> Find(Expression> filter, CancellationToken cancellationToken, bool track = false); diff --git a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs index 620f3eb601..78b4fedb75 100644 --- a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs @@ -33,14 +33,6 @@ public IdentitiesRepository(DevicesDbContext dbContext, UserManager> FindAll(PaginationFilter paginationFilter, CancellationToken cancellationToken) - { - var paginationResult = await _readonlyIdentities - .IncludeAll(_dbContext) - .OrderAndPaginate(d => d.CreatedAt, paginationFilter, cancellationToken); - return paginationResult; - } - public async Task FindByAddress(IdentityAddress address, CancellationToken cancellationToken, bool track = false) { return await (track ? _identities : _readonlyIdentities) @@ -54,7 +46,7 @@ public async Task Exists(IdentityAddress address, CancellationToken cancel .AnyAsync(i => i.Address == address, cancellationToken); } - public async Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false) + public async Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) { var query = (track ? _identities : _readonlyIdentities) .IncludeAll(_dbContext); @@ -62,6 +54,9 @@ public async Task> FindAllWithAddresses(IEnumerable addresses.Contains(i.Address)); + if (requestStatus != null) + query = query.Where(i => i.Status == requestStatus); + return await query .ToListAsync(cancellationToken); } diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs index a5f743fb36..c617d548ec 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs @@ -21,7 +21,7 @@ public Task Exists(IdentityAddress address, CancellationToken cancellation throw new NotImplementedException(); } - public Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false) + public Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) { throw new NotImplementedException(); } diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllFakeRepository.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs similarity index 86% rename from Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllFakeRepository.cs rename to Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs index f81fc79fe4..7f7677a36f 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllFakeRepository.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs @@ -7,11 +7,11 @@ namespace Backbone.Modules.Devices.Application.Tests.Tests.Identities.Queries.ListIdentities; -public class FindAllFakeRepository : IIdentitiesRepository +public class FindAllStubRepository : IIdentitiesRepository { private readonly IEnumerable _identities; - public FindAllFakeRepository(IEnumerable identities) + public FindAllStubRepository(IEnumerable identities) { _identities = identities; } @@ -21,13 +21,9 @@ public Task Exists(IdentityAddress address, CancellationToken cancellation throw new NotImplementedException(); } - public Task> FindAllWithAddresses(IEnumerable addresses, CancellationToken cancellationToken, bool track = false) + public Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) { - var result = _identities; - if (addresses.Any()) - result = result.Where(i => addresses.Contains(i.Address)); - - return Task.FromResult(result); + return Task.FromResult(_identities); } public Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs index bffae03182..89b750c702 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs @@ -1,4 +1,3 @@ -using Backbone.DevelopmentKit.Identity.ValueObjects; using Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; using Backbone.Modules.Devices.Domain.Entities.Identities; using Backbone.UnitTestTools.BaseClasses; @@ -15,17 +14,17 @@ public async Task Returns_an_empty_list_when_no_identities_exist() { // Arrange var identitiesList = new List(); - var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); + var handler = CreateHandler(new FindAllStubRepository(identitiesList)); // Act - var result = await handler.Handle(new ListIdentitiesQuery(new List()), CancellationToken.None); + var result = await handler.Handle(new ListIdentitiesQuery(), CancellationToken.None); // Assert result.Should().HaveCount(identitiesList.Count); } [Fact] - public async Task Returns_a_list_of_all_identities_when_addresses_list_is_empty() + public async Task? Returns_a_list_of_all_existing_identities() { // Arrange List identitiesList = @@ -42,45 +41,15 @@ public async Task Returns_a_list_of_all_identities_when_addresses_list_is_empty( TestDataGenerator.CreateRandomTierId(), 1) ]; - var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); + var handler = CreateHandler(new FindAllStubRepository(identitiesList)); // Act - var result = await handler.Handle(new ListIdentitiesQuery(new List()), CancellationToken.None); + var result = await handler.Handle(new ListIdentitiesQuery(), CancellationToken.None); // Assert result.Should().HaveCount(identitiesList.Count); } - - [Fact] - public async Task Returns_a_list_of_identities_with_matching_address_from_addresses_list_when_not_empty() - { - // Arrange - var firstIdentityAddress = CreateRandomIdentityAddress(); - List identitiesList = - [ - new Identity(CreateRandomDeviceId(), - firstIdentityAddress, - CreateRandomBytes(), - TestDataGenerator.CreateRandomTierId(), - 1), - - new Identity(CreateRandomDeviceId(), - CreateRandomIdentityAddress(), - CreateRandomBytes(), - TestDataGenerator.CreateRandomTierId(), - 1) - ]; - var addresses = new List { firstIdentityAddress }; - var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); - - // Act - var result = await handler.Handle(new ListIdentitiesQuery(addresses), CancellationToken.None); - - // Assert - result.Should().HaveCount(addresses.Count); - } - [Fact] public async Task Returned_identities_have_all_properties_filled_as_expected() { @@ -90,10 +59,10 @@ public async Task Returned_identities_have_all_properties_filled_as_expected() var expectedTierId = TestDataGenerator.CreateRandomTierId(); List identitiesList = [new Identity(expectedClientId, expectedAddress, [], expectedTierId, 1)]; - var handler = CreateHandler(new FindAllFakeRepository(identitiesList)); + var handler = CreateHandler(new FindAllStubRepository(identitiesList)); // Act - var result = await handler.Handle(new ListIdentitiesQuery(new List()), CancellationToken.None); + var result = await handler.Handle(new ListIdentitiesQuery(), CancellationToken.None); // Assert result.Should().HaveCount(1); @@ -105,8 +74,8 @@ public async Task Returned_identities_have_all_properties_filled_as_expected() result.First().IdentityVersion.Should().Be(1); } - private Handler CreateHandler(FindAllFakeRepository findAllFakeRepository) + private Handler CreateHandler(FindAllStubRepository findAllStubRepository) { - return new Handler(findAllFakeRepository); + return new Handler(findAllStubRepository); } } diff --git a/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs b/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs index e771b579c4..7402f651e9 100644 --- a/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs +++ b/Modules/Messages/src/Messages.ConsumerApi/Controllers/MessagesController.cs @@ -63,8 +63,7 @@ public async Task GetMessage(MessageId id, [FromQuery] bool? noBo public async Task SendMessage(SendMessageCommand request, CancellationToken cancellationToken) { var recipientAddresses = request.Recipients.Select(r => r.Address); - var recipientIdentities = await _mediator.Send(new ListIdentitiesQuery(recipientAddresses), cancellationToken); - var identitiesToBeDeleted = recipientIdentities.Where(i => i.Status == IdentityStatus.ToBeDeleted).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))); From ce9e65d0a80956349ff4a6a3423d8e0177524e59 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:29:29 +0100 Subject: [PATCH 05/12] test: exclude consumer api projects from arch unit tests --- Backbone.Tests.ArchUnit/CleanArchitecture.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Backbone.Tests.ArchUnit/CleanArchitecture.cs b/Backbone.Tests.ArchUnit/CleanArchitecture.cs index 91aa76ec3a..9b1895537a 100644 --- a/Backbone.Tests.ArchUnit/CleanArchitecture.cs +++ b/Backbone.Tests.ArchUnit/CleanArchitecture.cs @@ -12,6 +12,11 @@ public class CleanArchitecture .ResideInAssembly("Backbone.Modules.*", true) .As("All Modules"); + private static readonly IObjectProvider CONSUMER_API_ASSEMBLIES = + Types().That() + .ResideInAssembly("Backbone.Modules.*.ConsumerApi", true) + .As("ConsumerApi Assemblies"); + private static readonly IObjectProvider APPLICATION_ASSEMBLIES = Types().That() .ResideInAssembly("Backbone.Modules.*.Application", true) @@ -34,6 +39,7 @@ public void ModulesShouldNotDependOnOtherModules(IObjectProvider module) Types() .That().Are(module) .And().AreNot(Backbone.TEST_TYPES) + .And().AreNot(CONSUMER_API_ASSEMBLIES) .Should().NotDependOnAny(otherModules) .Because("modules should be self-contained.") .Check(Backbone.ARCHITECTURE); From b56168188fbe6a563f56100e999498ee0310f034 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:03:05 +0100 Subject: [PATCH 06/12] test: add integration tests for messages controller (send messages) --- .../Endpoints/Common/Types/ApiError.cs | 1 + .../Features/Messages/POST.feature | 18 +++ .../MessagesStepDefinitions.cs | 118 ++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 ConsumerApi.Tests.Integration/Features/Messages/POST.feature create mode 100644 ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs diff --git a/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs b/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs index 861252dcf0..d5ce98045a 100644 --- a/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs +++ b/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs @@ -7,4 +7,5 @@ public class ApiError public required string Message { get; set; } public required string Docs { get; set; } public required DateTime Time { get; set; } + public dynamic? Data { get; set; } } diff --git a/ConsumerApi.Tests.Integration/Features/Messages/POST.feature b/ConsumerApi.Tests.Integration/Features/Messages/POST.feature new file mode 100644 index 0000000000..74722d517a --- /dev/null +++ b/ConsumerApi.Tests.Integration/Features/Messages/POST.feature @@ -0,0 +1,18 @@ +@Integration +Feature: POST Message + +User sends a Message + +Scenario: Sending a Message + Given Identities i1 and i2 with an established Relationship + When a POST request is sent to the /Messages endpoint + Then the response status code is 201 (Created) + And the response contains a CreateMessageResponse + +Scenario: Sending a Message to Identity to be deleted + Given Identities i1 and i2 with an established Relationship + And Identity i2 is to be deleted + When a POST request is sent to the /Messages endpoint + 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 Identity i2 diff --git a/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs new file mode 100644 index 0000000000..12f954c19c --- /dev/null +++ b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs @@ -0,0 +1,118 @@ +using System.Text.Json; +using Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; +using Backbone.ConsumerApi.Sdk; +using Backbone.ConsumerApi.Sdk.Authentication; +using Backbone.ConsumerApi.Sdk.Endpoints.Messages.Types.Requests; +using Backbone.ConsumerApi.Sdk.Endpoints.Messages.Types.Responses; +using Backbone.ConsumerApi.Sdk.Endpoints.Relationships.Types.Requests; +using Backbone.ConsumerApi.Sdk.Endpoints.RelationshipTemplates.Types.Requests; +using Backbone.ConsumerApi.Tests.Integration.Configuration; +using Backbone.ConsumerApi.Tests.Integration.Extensions; +using Backbone.ConsumerApi.Tests.Integration.Support; +using Backbone.Crypto; +using Microsoft.Extensions.Options; + +namespace Backbone.ConsumerApi.Tests.Integration.StepDefinitions; + +[Binding] +[Scope(Feature = "POST Message")] +internal class MessagesStepDefinitions +{ + private Client _sdk1 = null!; + private Client _sdk2 = null!; + private ApiResponse? _sendMessageResponse; + private readonly ClientCredentials _clientCredentials; + private readonly HttpClient _httpClient; + + public MessagesStepDefinitions(HttpClientFactory factory, IOptions httpConfiguration) + { + _httpClient = factory.CreateClient(); + _clientCredentials = new ClientCredentials(httpConfiguration.Value.ClientCredentials.ClientId, httpConfiguration.Value.ClientCredentials.ClientSecret); + } + + [Given("Identities i1 and i2 with an established Relationship")] + public async Task GivenIdentitiesI1AndI2WithAnEstablishedRelationship() + { + _sdk1 = await Client.CreateForNewIdentity(_httpClient, _clientCredentials, Constants.DEVICE_PASSWORD); + _sdk2 = await Client.CreateForNewIdentity(_httpClient, _clientCredentials, Constants.DEVICE_PASSWORD); + + var createRelationshipTemplateRequest = new CreateRelationshipTemplateRequest + { + Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + }; + + var relationshipTemplateResponse = await _sdk1.RelationshipTemplates.CreateTemplate(createRelationshipTemplateRequest); + relationshipTemplateResponse.Should().BeASuccess(); + + var createRelationshipRequest = new CreateRelationshipRequest + { + RelationshipTemplateId = relationshipTemplateResponse.Result!.Id, + Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + }; + + var createRelationshipResponse = await _sdk2.Relationships.CreateRelationship(createRelationshipRequest); + createRelationshipResponse.Should().BeASuccess(); + + var completeRelationshipChangeRequest = new CompleteRelationshipChangeRequest + { + Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + }; + var acceptRelationChangeResponse = + await _sdk1.Relationships.AcceptChange(createRelationshipResponse.Result!.Id, createRelationshipResponse.Result.Changes.First().Id, completeRelationshipChangeRequest); + acceptRelationChangeResponse.Should().BeASuccess(); + } + + [Given("Identity i2 is to be deleted")] + public async Task GivenIdentityI2IsToBeDeleted() + { + var startDeletionProcessResponse = await _sdk2.Identities.StartDeletionProcess(); + startDeletionProcessResponse.Should().BeASuccess(); + } + + [When("a POST request is sent to the /Messages endpoint")] + public async Task WhenAPostRequestIsSentToTheMessagesEndpoint() + { + var sendMessageRequest = new SendMessageRequest + { + Attachments = [], + Body = ConvertibleString.FromUtf8("Some Message").BytesRepresentation, + Recipients = + [ + new SendMessageRequestRecipientInformation + { + Address = _sdk2.IdentityData!.Address, + EncryptedKey = ConvertibleString.FromUtf8("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").BytesRepresentation + } + ] + }; + _sendMessageResponse = await _sdk1.Messages.SendMessage(sendMessageRequest); + } + + [Then(@"the response status code is (\d\d\d) \(.+\)")] + public void ThenTheResponseStatusCodeIs(int expectedStatusCode) + { + ((int)_sendMessageResponse!.Status).Should().Be(expectedStatusCode); + } + + [Then("the response contains a CreateMessageResponse")] + public void ThenTheResponseContainsADeletionProcess() + { + _sendMessageResponse!.Result.Should().NotBeNull(); + _sendMessageResponse.Should().BeASuccess(); + _sendMessageResponse.Should().ComplyWithSchema(); + } + + [Then(@"the response content contains an error with the error code ""([^""]*)""")] + public void ThenTheResponseContentIncludesAnErrorWithTheErrorCode(string errorCode) + { + _sendMessageResponse!.Error.Should().NotBeNull(); + _sendMessageResponse.Error!.Code.Should().Be(errorCode); + } + + [Then(@"the error contains a list of identities to be deleted that includes Identity i2")] + public void ThenTheErrorContainsAListOfIdentitiesToBeDeletedThatIncludesIdentityI2() + { + var peersToBeDeleted = ((JsonElement)_sendMessageResponse!.Error!.Data!).Deserialize>(); + peersToBeDeleted!.Contains(_sdk2.IdentityData!.Address).Should().BeTrue(); + } +} From 22d5adb22b1a4f638f3d34785b7b87141f4096d2 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:13:16 +0100 Subject: [PATCH 07/12] refactor: use expression for identitiy filtering --- Backbone.Tests.ArchUnit/CleanArchitecture.cs | 3 +- .../Features/Messages/POST.feature | 10 +-- .../Helpers/Utils.cs | 38 ++++++++++++ .../MessagesStepDefinitions.cs | 61 +++++++------------ .../Queries/ListIdentities/Handler.cs | 7 ++- .../Repository/IIdentitiesRepository.cs | 2 - .../Repository/IdentitiesRepository.cs | 15 ----- .../FindByAddressStubRepository.cs | 2 +- .../ListIdentities/FindAllStubRepository.cs | 17 +----- .../Queries/ListIdentities/HandlerTests.cs | 16 ++--- .../Messages.Application/ApplicationErrors.cs | 4 +- 11 files changed, 84 insertions(+), 91 deletions(-) create mode 100644 ConsumerApi.Tests.Integration/Helpers/Utils.cs diff --git a/Backbone.Tests.ArchUnit/CleanArchitecture.cs b/Backbone.Tests.ArchUnit/CleanArchitecture.cs index 9b1895537a..906ae87429 100644 --- a/Backbone.Tests.ArchUnit/CleanArchitecture.cs +++ b/Backbone.Tests.ArchUnit/CleanArchitecture.cs @@ -14,7 +14,8 @@ public class CleanArchitecture private static readonly IObjectProvider CONSUMER_API_ASSEMBLIES = Types().That() - .ResideInAssembly("Backbone.Modules.*.ConsumerApi", true) + .Are(MODULES) + .And().HaveNameEndingWith(".ConsumerApi") .As("ConsumerApi Assemblies"); private static readonly IObjectProvider APPLICATION_ASSEMBLIES = diff --git a/ConsumerApi.Tests.Integration/Features/Messages/POST.feature b/ConsumerApi.Tests.Integration/Features/Messages/POST.feature index 74722d517a..98adf73139 100644 --- a/ConsumerApi.Tests.Integration/Features/Messages/POST.feature +++ b/ConsumerApi.Tests.Integration/Features/Messages/POST.feature @@ -5,14 +5,14 @@ User sends a Message Scenario: Sending a Message Given Identities i1 and i2 with an established Relationship - When a POST request is sent to the /Messages endpoint + When i1 sends a POST request to the /Messages endpoint with i2 as recipient Then the response status code is 201 (Created) - And the response contains a CreateMessageResponse + And the response contains a SendMessageResponse Scenario: Sending a Message to Identity to be deleted Given Identities i1 and i2 with an established Relationship - And Identity i2 is to be deleted - When a POST request is sent to the /Messages endpoint + 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 Identity i2 + And the error contains a list of Identities to be deleted that includes i2 diff --git a/ConsumerApi.Tests.Integration/Helpers/Utils.cs b/ConsumerApi.Tests.Integration/Helpers/Utils.cs new file mode 100644 index 0000000000..dc80a43546 --- /dev/null +++ b/ConsumerApi.Tests.Integration/Helpers/Utils.cs @@ -0,0 +1,38 @@ +using Backbone.ConsumerApi.Sdk; +using Backbone.ConsumerApi.Sdk.Endpoints.Relationships.Types.Requests; +using Backbone.ConsumerApi.Sdk.Endpoints.RelationshipTemplates.Types.Requests; +using Backbone.ConsumerApi.Tests.Integration.Extensions; +using Backbone.Crypto; + +namespace Backbone.ConsumerApi.Tests.Integration.Helpers; + +public static class Utils +{ + public static async Task EstablishRelationshipBetween(Client client1, Client client2) + { + var createRelationshipTemplateRequest = new CreateRelationshipTemplateRequest + { + Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + }; + + var relationshipTemplateResponse = await client1.RelationshipTemplates.CreateTemplate(createRelationshipTemplateRequest); + relationshipTemplateResponse.Should().BeASuccess(); + + var createRelationshipRequest = new CreateRelationshipRequest + { + RelationshipTemplateId = relationshipTemplateResponse.Result!.Id, + Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + }; + + var createRelationshipResponse = await client2.Relationships.CreateRelationship(createRelationshipRequest); + createRelationshipResponse.Should().BeASuccess(); + + var completeRelationshipChangeRequest = new CompleteRelationshipChangeRequest + { + Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + }; + var acceptRelationChangeResponse = + await client1.Relationships.AcceptChange(createRelationshipResponse.Result!.Id, createRelationshipResponse.Result.Changes.First().Id, completeRelationshipChangeRequest); + acceptRelationChangeResponse.Should().BeASuccess(); + } +} diff --git a/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs index 12f954c19c..717d0b4e83 100644 --- a/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs +++ b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs @@ -4,10 +4,9 @@ using Backbone.ConsumerApi.Sdk.Authentication; using Backbone.ConsumerApi.Sdk.Endpoints.Messages.Types.Requests; using Backbone.ConsumerApi.Sdk.Endpoints.Messages.Types.Responses; -using Backbone.ConsumerApi.Sdk.Endpoints.Relationships.Types.Requests; -using Backbone.ConsumerApi.Sdk.Endpoints.RelationshipTemplates.Types.Requests; using Backbone.ConsumerApi.Tests.Integration.Configuration; using Backbone.ConsumerApi.Tests.Integration.Extensions; +using Backbone.ConsumerApi.Tests.Integration.Helpers; using Backbone.ConsumerApi.Tests.Integration.Support; using Backbone.Crypto; using Microsoft.Extensions.Options; @@ -18,8 +17,8 @@ namespace Backbone.ConsumerApi.Tests.Integration.StepDefinitions; [Scope(Feature = "POST Message")] internal class MessagesStepDefinitions { - private Client _sdk1 = null!; - private Client _sdk2 = null!; + private Client _client1 = null!; + private Client _client2 = null!; private ApiResponse? _sendMessageResponse; private readonly ClientCredentials _clientCredentials; private readonly HttpClient _httpClient; @@ -33,43 +32,20 @@ public MessagesStepDefinitions(HttpClientFactory factory, IOptions>(); - peersToBeDeleted!.Contains(_sdk2.IdentityData!.Address).Should().BeTrue(); + var data = ((JsonElement)_sendMessageResponse!.Error!.Data!).Deserialize(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + data!.PeersToBeDeleted.Contains(_client2.IdentityData!.Address).Should().BeTrue(); } } + +public class SendMessageErrorData +{ + public List PeersToBeDeleted { get; set; } = []; +} diff --git a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs index c11339e432..81cef6694e 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs @@ -1,5 +1,7 @@ +using System.Linq.Expressions; using Backbone.Modules.Devices.Application.DTOs; using Backbone.Modules.Devices.Application.Infrastructure.Persistence.Repository; +using Backbone.Modules.Devices.Domain.Entities.Identities; using MediatR; namespace Backbone.Modules.Devices.Application.Identities.Queries.ListIdentities; @@ -15,7 +17,10 @@ public Handler(IIdentitiesRepository repository) public async Task Handle(ListIdentitiesQuery request, CancellationToken cancellationToken) { - var identities = await _identitiesRepository.FindAllWithAddresses(cancellationToken, request.Addresses, request.Status); + Expression> filter = i => (request.Addresses == null || request.Addresses.Contains(i.Address)) + && (request.Status == null || i.Status == request.Status); + + var identities = await _identitiesRepository.Find(filter, cancellationToken); var identityDtos = identities.Select(el => new IdentitySummaryDTO(el)).ToList(); return new ListIdentitiesResponse(identityDtos); diff --git a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs index 7470d106c4..503afeb37c 100644 --- a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs @@ -9,11 +9,9 @@ namespace Backbone.Modules.Devices.Application.Infrastructure.Persistence.Reposi public interface IIdentitiesRepository { #region Identities - Task Update(Identity identity, CancellationToken cancellationToken); Task FindByAddress(IdentityAddress address, CancellationToken cancellationToken, bool track = false); Task Exists(IdentityAddress address, CancellationToken cancellationToken); - Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses = null, IdentityStatus? requestStatus = null, bool track = false); Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false); Task CountByClientId(string clientId, CancellationToken cancellationToken); Task> Find(Expression> filter, CancellationToken cancellationToken, bool track = false); diff --git a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs index 78b4fedb75..4a302a4b7f 100644 --- a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs @@ -46,21 +46,6 @@ public async Task Exists(IdentityAddress address, CancellationToken cancel .AnyAsync(i => i.Address == address, cancellationToken); } - public async Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) - { - var query = (track ? _identities : _readonlyIdentities) - .IncludeAll(_dbContext); - - if (addresses.Any()) - query = query.Where(i => addresses.Contains(i.Address)); - - if (requestStatus != null) - query = query.Where(i => i.Status == requestStatus); - - return await query - .ToListAsync(cancellationToken); - } - public async Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) { return await (track ? _identities : _readonlyIdentities) diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs index c617d548ec..d07ff2bc68 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs @@ -21,7 +21,7 @@ public Task Exists(IdentityAddress address, CancellationToken cancellation throw new NotImplementedException(); } - public Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) + public Task> Find(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) { throw new NotImplementedException(); } diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs index 7f7677a36f..2b92099b5d 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/FindAllStubRepository.cs @@ -21,11 +21,6 @@ public Task Exists(IdentityAddress address, CancellationToken cancellation throw new NotImplementedException(); } - public Task> FindAllWithAddresses(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) - { - return Task.FromResult(_identities); - } - public Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) { throw new NotImplementedException(); @@ -41,11 +36,6 @@ public Task AddUser(ApplicationUser user, string password) throw new NotImplementedException(); } - public Task> FindAllWithDeletionProcessWaitingForApproval(CancellationToken cancellationToken, bool track = false) - { - throw new NotImplementedException(); - } - public Task> FindAllDevicesOfIdentity(IdentityAddress identity, IEnumerable ids, PaginationFilter paginationFilter, CancellationToken cancellationToken) { throw new NotImplementedException(); @@ -71,14 +61,9 @@ public Task Update(Identity identity, CancellationToken cancellationToken) throw new NotImplementedException(); } - public Task> FindAllWithPastDeletionGracePeriod(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - public Task> Find(Expression> filter, CancellationToken cancellationToken, bool track = false) { - throw new NotImplementedException(); + return Task.FromResult(_identities); } public Task Delete(Expression> filter, CancellationToken cancellationToken) diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs index 89b750c702..a2ea0c1ca0 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/ListIdentities/HandlerTests.cs @@ -13,21 +13,21 @@ public class HandlerTests : AbstractTestsBase public async Task Returns_an_empty_list_when_no_identities_exist() { // Arrange - var identitiesList = new List(); - var handler = CreateHandler(new FindAllStubRepository(identitiesList)); + var identities = new List(); + var handler = CreateHandler(new FindAllStubRepository(identities)); // Act var result = await handler.Handle(new ListIdentitiesQuery(), CancellationToken.None); // Assert - result.Should().HaveCount(identitiesList.Count); + result.Should().HaveCount(identities.Count); } [Fact] public async Task? Returns_a_list_of_all_existing_identities() { // Arrange - List identitiesList = + List identities = [ new Identity(CreateRandomDeviceId(), CreateRandomIdentityAddress(), @@ -41,13 +41,13 @@ public async Task Returns_an_empty_list_when_no_identities_exist() TestDataGenerator.CreateRandomTierId(), 1) ]; - var handler = CreateHandler(new FindAllStubRepository(identitiesList)); + var handler = CreateHandler(new FindAllStubRepository(identities)); // Act var result = await handler.Handle(new ListIdentitiesQuery(), CancellationToken.None); // Assert - result.Should().HaveCount(identitiesList.Count); + result.Should().HaveCount(identities.Count); } [Fact] @@ -57,9 +57,9 @@ public async Task Returned_identities_have_all_properties_filled_as_expected() var expectedClientId = CreateRandomDeviceId(); var expectedAddress = CreateRandomIdentityAddress(); var expectedTierId = TestDataGenerator.CreateRandomTierId(); - List identitiesList = [new Identity(expectedClientId, expectedAddress, [], expectedTierId, 1)]; + List identities = [new Identity(expectedClientId, expectedAddress, [], expectedTierId, 1)]; - var handler = CreateHandler(new FindAllStubRepository(identitiesList)); + var handler = CreateHandler(new FindAllStubRepository(identities)); // Act var result = await handler.Handle(new ListIdentitiesQuery(), CancellationToken.None); diff --git a/Modules/Messages/src/Messages.Application/ApplicationErrors.cs b/Modules/Messages/src/Messages.Application/ApplicationErrors.cs index 45362d7190..c759f97632 100644 --- a/Modules/Messages/src/Messages.Application/ApplicationErrors.cs +++ b/Modules/Messages/src/Messages.Application/ApplicationErrors.cs @@ -7,8 +7,8 @@ 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 their identity is set to be deleted.", - peersToBeDeleted); + $"Cannot send message to {peersToBeDeleted.Count()} of the recipients because they are in status 'ToBeDeleted'.", + new { PeersToBeDeleted = peersToBeDeleted }); } public static ApplicationError NoRelationshipToRecipientExists(string recipient = "") From 6c6a112ef1bd8ca5a0228c32ab6f48c39cc11986 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:18:07 +0100 Subject: [PATCH 08/12] fix: arch unit tests --- Backbone.Tests.ArchUnit/CleanArchitecture.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Backbone.Tests.ArchUnit/CleanArchitecture.cs b/Backbone.Tests.ArchUnit/CleanArchitecture.cs index 906ae87429..9b1895537a 100644 --- a/Backbone.Tests.ArchUnit/CleanArchitecture.cs +++ b/Backbone.Tests.ArchUnit/CleanArchitecture.cs @@ -14,8 +14,7 @@ public class CleanArchitecture private static readonly IObjectProvider CONSUMER_API_ASSEMBLIES = Types().That() - .Are(MODULES) - .And().HaveNameEndingWith(".ConsumerApi") + .ResideInAssembly("Backbone.Modules.*.ConsumerApi", true) .As("ConsumerApi Assemblies"); private static readonly IObjectProvider APPLICATION_ASSEMBLIES = From 6547a016b8e14658c1005fbe82100648425fb7c2 Mon Sep 17 00:00:00 2001 From: Timo Notheisen Date: Mon, 10 Jun 2024 15:54:36 +0200 Subject: [PATCH 09/12] test: try to make deserialization of data reusable --- .../Endpoints/Common/EndpointClient.cs | 2 - .../Endpoints/Common/Types/ApiError.cs | 39 ++++++++++++++++++- ConsumerApi.Sdk/Configuration.cs | 11 +++++- .../MessagesStepDefinitions.cs | 6 +-- 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/EndpointClient.cs b/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/EndpointClient.cs index ff1dd1aad1..49c9e18a0d 100644 --- a/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/EndpointClient.cs +++ b/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/EndpointClient.cs @@ -5,7 +5,6 @@ using System.Text.Json; using System.Web; using Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; -using Backbone.Tooling.JsonConverters; using JsonSerializer = System.Text.Json.JsonSerializer; namespace Backbone.BuildingBlocks.SDK.Endpoints.Common; @@ -34,7 +33,6 @@ public EndpointClient(HttpClient httpClient, IAuthenticator authenticator, JsonS _httpClient = httpClient; _authenticator = authenticator; _jsonSerializerOptions = jsonSerializerOptions; - jsonSerializerOptions.Converters.Add(new UrlSafeBase64ToByteArrayJsonConverter()); } public async Task> Post(string url, object? requestContent = null) diff --git a/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs b/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs index d5ce98045a..2b105083e4 100644 --- a/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs +++ b/BuildingBlocks/src/BuildingBlocks.SDK/Endpoints/Common/Types/ApiError.cs @@ -1,4 +1,7 @@ -namespace Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; public class ApiError { @@ -7,5 +10,37 @@ public class ApiError public required string Message { get; set; } public required string Docs { get; set; } public required DateTime Time { get; set; } - public dynamic? Data { get; set; } + public ApiErrorData? Data { get; set; } +} + +public class ApiErrorData +{ + private readonly JsonElement _data; + private readonly JsonSerializerOptions _optionsUsedToDeserializeThis; + + private ApiErrorData(JsonElement data, JsonSerializerOptions options) + { + _data = data; + _optionsUsedToDeserializeThis = options; // we need to keep the options to be able to deserialize the data later + } + + public T As() + { + var json = _data.GetRawText(); + return JsonSerializer.Deserialize(json, _optionsUsedToDeserializeThis)!; + } + + public class JsonConverter : JsonConverter + { + public override ApiErrorData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var element = JsonSerializer.Deserialize(ref reader, options); + return new ApiErrorData(element, options); + } + + public override void Write(Utf8JsonWriter writer, ApiErrorData value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.As(), options); + } + } } diff --git a/ConsumerApi.Sdk/Configuration.cs b/ConsumerApi.Sdk/Configuration.cs index f5758ab9a2..0b995d1d0e 100644 --- a/ConsumerApi.Sdk/Configuration.cs +++ b/ConsumerApi.Sdk/Configuration.cs @@ -1,12 +1,21 @@ using System.Text.Json; +using Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; using Backbone.ConsumerApi.Sdk.Authentication; +using Backbone.Tooling.JsonConverters; namespace Backbone.ConsumerApi.Sdk; public class Configuration { + public Configuration() + { + JsonSerializerOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + JsonSerializerOptions.Converters.Add(new UrlSafeBase64ToByteArrayJsonConverter()); + JsonSerializerOptions.Converters.Add(new ApiErrorData.JsonConverter()); + } + public required AuthenticationConfiguration Authentication { get; init; } - public JsonSerializerOptions JsonSerializerOptions { get; init; } = new() { PropertyNameCaseInsensitive = true }; + public JsonSerializerOptions JsonSerializerOptions { get; init; } public class AuthenticationConfiguration { diff --git a/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs index 717d0b4e83..a4439fc3dd 100644 --- a/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs +++ b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs @@ -1,5 +1,4 @@ -using System.Text.Json; -using Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; +using Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; using Backbone.ConsumerApi.Sdk; using Backbone.ConsumerApi.Sdk.Authentication; using Backbone.ConsumerApi.Sdk.Endpoints.Messages.Types.Requests; @@ -88,7 +87,8 @@ public void ThenTheResponseContentIncludesAnErrorWithTheErrorCode(string errorCo [Then(@"the error contains a list of Identities to be deleted that includes i2")] public void ThenTheErrorContainsAListOfIdentitiesToBeDeletedThatIncludesIdentityI2() { - var data = ((JsonElement)_sendMessageResponse!.Error!.Data!).Deserialize(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + var data = _sendMessageResponse!.Error!.Data?.As(); + data.Should().NotBeNull(); data!.PeersToBeDeleted.Contains(_client2.IdentityData!.Address).Should().BeTrue(); } } From 321ebc75c2bdd6a536b53b15eed125df52059067 Mon Sep 17 00:00:00 2001 From: Timo Notheisen Date: Mon, 10 Jun 2024 16:25:23 +0200 Subject: [PATCH 10/12] test: make PeersToBeDeleted property required and rename class --- .../StepDefinitions/MessagesStepDefinitions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs index a4439fc3dd..d83cc389cb 100644 --- a/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs +++ b/ConsumerApi.Tests.Integration/StepDefinitions/MessagesStepDefinitions.cs @@ -87,13 +87,13 @@ public void ThenTheResponseContentIncludesAnErrorWithTheErrorCode(string errorCo [Then(@"the error contains a list of Identities to be deleted that includes i2")] public void ThenTheErrorContainsAListOfIdentitiesToBeDeletedThatIncludesIdentityI2() { - var data = _sendMessageResponse!.Error!.Data?.As(); + var data = _sendMessageResponse!.Error!.Data?.As(); data.Should().NotBeNull(); data!.PeersToBeDeleted.Contains(_client2.IdentityData!.Address).Should().BeTrue(); } } -public class SendMessageErrorData +public class PeersToBeDeletedErrorData { - public List PeersToBeDeleted { get; set; } = []; + public required List PeersToBeDeleted { get; set; } } From cd5d140564e6bacd0887d59b3702c5efa2f91c04 Mon Sep 17 00:00:00 2001 From: Timo Notheisen Date: Mon, 10 Jun 2024 16:34:14 +0200 Subject: [PATCH 11/12] chore: formatting --- .../Identities/Queries/ListIdentities/Handler.cs | 6 +++--- .../Persistence/Repository/IdentitiesRepository.cs | 6 ++---- .../Queries/GetIdentity/FindByAddressStubRepository.cs | 5 ----- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs index 81cef6694e..fa77915539 100644 --- a/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Identities/Queries/ListIdentities/Handler.cs @@ -17,11 +17,11 @@ public Handler(IIdentitiesRepository repository) public async Task Handle(ListIdentitiesQuery request, CancellationToken cancellationToken) { - Expression> filter = i => (request.Addresses == null || request.Addresses.Contains(i.Address)) - && (request.Status == null || i.Status == request.Status); + Expression> filter = i => (request.Addresses == null || request.Addresses.Contains(i.Address)) && + (request.Status == null || i.Status == request.Status); var identities = await _identitiesRepository.Find(filter, cancellationToken); - var identityDtos = identities.Select(el => new IdentitySummaryDTO(el)).ToList(); + var identityDtos = identities.Select(i => new IdentitySummaryDTO(i)).ToList(); return new ListIdentitiesResponse(identityDtos); } diff --git a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs index 439345dcac..334ca0d7a3 100644 --- a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs @@ -51,8 +51,7 @@ public async Task> GetIdentity public async Task Exists(IdentityAddress address, CancellationToken cancellationToken) { - return await _readonlyIdentities - .AnyAsync(i => i.Address == address, cancellationToken); + return await _readonlyIdentities.AnyAsync(i => i.Address == address, cancellationToken); } public async Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) @@ -65,8 +64,7 @@ public async Task> FindAllWithDeletionProcessInStatus(Dele public async Task CountByClientId(string clientId, CancellationToken cancellationToken) { - return await _readonlyIdentities - .CountAsync(i => i.ClientId == clientId, cancellationToken); + return await _readonlyIdentities.CountAsync(i => i.ClientId == clientId, cancellationToken); } public async Task AddUser(ApplicationUser user, string password) diff --git a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs index 312d9d7e86..2bb2977be9 100644 --- a/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs +++ b/Modules/Devices/test/Devices.Application.Tests/Tests/Identities/Queries/GetIdentity/FindByAddressStubRepository.cs @@ -21,11 +21,6 @@ public Task Exists(IdentityAddress address, CancellationToken cancellation throw new NotImplementedException(); } - public Task> Find(CancellationToken cancellationToken, IEnumerable? addresses, IdentityStatus? requestStatus, bool track = false) - { - throw new NotImplementedException(); - } - public Task> FindAllWithDeletionProcessInStatus(DeletionProcessStatus status, CancellationToken cancellationToken, bool track = false) { throw new NotImplementedException(); From d428f0b884c2f229b857cea12cfe1002664e8f12 Mon Sep 17 00:00:00 2001 From: Daniel Almeida <115644988+daniel-almeida-konkconsulting@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:52:10 +0100 Subject: [PATCH 12/12] fix: update npm packages with vulnerabilties --- AdminApi/src/AdminApi/ClientApp/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/AdminApi/src/AdminApi/ClientApp/package-lock.json b/AdminApi/src/AdminApi/ClientApp/package-lock.json index a1d2d0d177..2f8b19386e 100644 --- a/AdminApi/src/AdminApi/ClientApp/package-lock.json +++ b/AdminApi/src/AdminApi/ClientApp/package-lock.json @@ -6226,12 +6226,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -8491,9 +8491,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1"