From a61fa8a1cd6bf08d0440de46b059ee558b7efbf3 Mon Sep 17 00:00:00 2001 From: jvandaal Date: Tue, 14 May 2024 17:04:22 +0200 Subject: [PATCH 01/13] feat: consume StreetNameWasReaddressed + create command --- ParcelRegistry.sln.DotSettings | 2 + paket.dependencies | 20 +++--- paket.lock | 32 +++++----- .../CommandHandlingKafkaProjection.cs | 60 +++++++++++++++++- .../Parcel/Commands/ReaddressAddresses.cs | 63 +++++++++++++++++++ 5 files changed, 147 insertions(+), 30 deletions(-) create mode 100644 src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs diff --git a/ParcelRegistry.sln.DotSettings b/ParcelRegistry.sln.DotSettings index c38e744a..37da9169 100644 --- a/ParcelRegistry.sln.DotSettings +++ b/ParcelRegistry.sln.DotSettings @@ -14,6 +14,7 @@ True True <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aa_bb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aa_bb" /></Policy></Policy> True Never Never @@ -26,4 +27,5 @@ True True True + True diff --git a/paket.dependencies b/paket.dependencies index 50282344..7dc15eaf 100755 --- a/paket.dependencies +++ b/paket.dependencies @@ -79,16 +79,16 @@ nuget Be.Vlaanderen.Basisregisters.Projector 15.0.0 nuget Be.Vlaanderen.Basisregisters.Crab 4.0.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Common 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.1.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.1.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Common 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.5.0 nuget Be.Vlaanderen.Basisregisters.MessageHandling.AwsSqs.Simple 5.0.1 nuget Be.Vlaanderen.Basisregisters.MessageHandling.Kafka.Simple 5.0.1 diff --git a/paket.lock b/paket.lock index d5c41856..8d12dec6 100644 --- a/paket.lock +++ b/paket.lock @@ -253,20 +253,18 @@ NUGET Autofac.Extensions.DependencyInjection (>= 9.0) Be.Vlaanderen.Basisregisters.EventHandling (5.0) Be.Vlaanderen.Basisregisters.Generators.Guid.Deterministic (4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) Be.Vlaanderen.Basisregisters.AggregateSource (>= 9.0.1) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) NetTopologySuite (>= 2.5) NodaTime (>= 3.1.11) - Be.Vlaanderen.Basisregisters.GrAr.Contracts (21.1) - Be.Vlaanderen.Basisregisters.AggregateSource (>= 9.0.1) - NodaTime (>= 3.1.11) - Be.Vlaanderen.Basisregisters.GrAr.Edit (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Contracts (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Edit (21.5) NetTopologySuite (>= 2.5) - Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.5) Be.Vlaanderen.Basisregisters.Api (>= 21.0) Be.Vlaanderen.Basisregisters.Shaperon (>= 10.0.2) - Be.Vlaanderen.Basisregisters.GrAr.Import (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Import (21.5) Autofac (>= 8.0) Be.Vlaanderen.Basisregisters.AggregateSource.SqlStreamStore (>= 9.0.1) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) @@ -281,31 +279,31 @@ NUGET Serilog (>= 3.1.1) Serilog.Extensions.Logging (>= 8.0) System.Threading.Tasks.Dataflow (>= 8.0) - Be.Vlaanderen.Basisregisters.GrAr.Legacy (21.1) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Legacy (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) Be.Vlaanderen.Basisregisters.Utilities.Rfc3339DateTimeOffset (>= 4.0) Newtonsoft.Json (>= 13.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.5) AWSSDK.SimpleNotificationService (>= 3.7.301.3) System.Text.Json (>= 8.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.5) Be.Vlaanderen.Basisregisters.AspNetCore.Mvc.Formatters.Json (>= 5.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) Be.Vlaanderen.Basisregisters.Utilities.Rfc3339DateTimeOffset (>= 4.0) Microsoft.Extensions.Configuration (>= 8.0) Microsoft.Extensions.Http.Polly (>= 8.0.3) Newtonsoft.Json (>= 13.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.5) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) Be.Vlaanderen.Basisregisters.Crab (>= 4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) Microsoft.CSharp (>= 4.7) - Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.5) Be.Vlaanderen.Basisregisters.Auth.AcmIdm (>= 2.0) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) Be.Vlaanderen.Basisregisters.Crab (>= 4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.1) - Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.1) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.5) Microsoft.CSharp (>= 4.7) Be.Vlaanderen.Basisregisters.MessageHandling.AwsSqs.Simple (5.0.1) AWSSDK.Core (>= 3.7.302.15) diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 3126ad0a..2cc4d822 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -1,6 +1,7 @@ namespace ParcelRegistry.Consumer.Address.Projections { using System; + using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -159,6 +160,59 @@ await DetachBecauseRemoved( ct); }); + When(async (commandHandler, message, ct) => + { + await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); + + var readdresses = message.ReaddressedHouseNumbers + .Select(x => new ReaddressData( + new AddressPersistentLocalId(x.ReaddressedHouseNumber.SourceAddressPersistentLocalId), + new AddressPersistentLocalId(x.ReaddressedHouseNumber.DestinationAddressPersistentLocalId))) + .Concat( + message.ReaddressedHouseNumbers + .SelectMany(x => x.ReaddressedBoxNumbers) + .Select(boxNumberAddress => new ReaddressData( + new AddressPersistentLocalId(boxNumberAddress.SourceAddressPersistentLocalId), + new AddressPersistentLocalId(boxNumberAddress.DestinationAddressPersistentLocalId)))) + .ToList(); + /* + * We krijgen allemaal adres ids: bron en doel adressen + * Met alle bronadressen zoeken we alle unieke percelen op + * Stuur naar ieder perceel alle herkoppelingen + */ + var parcels = new Dictionary>(); + foreach (var readdress in readdresses) + { + var relations = backOfficeContext.ParcelAddressRelations + .AsNoTracking() + .Where(x => + x.AddressPersistentLocalId == readdress.SourceAddressPersistentLocalId) + .ToList(); + + foreach (var parcelAddressRelation in relations) + { + if (parcels.TryGetValue(parcelAddressRelation.ParcelId, out var addresses)) + { + addresses.Add(readdress); + } + else + { + parcels[parcelAddressRelation.ParcelId] = [readdress]; + } + } + } + + foreach (var parcel in parcels) + { + var command = new ReaddressAddresses( + new ParcelId(parcel.Key), + parcel.Value, + FromProvenance(message.Provenance)); + + await commandHandler.Handle(command, ct); + } + }); + When(async (commandHandler, message, ct) => { await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); @@ -264,9 +318,9 @@ private async Task DetachBecauseRetired( { await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); var relations = backOfficeContext.ParcelAddressRelations - .AsNoTracking() - .Where(x => x.AddressPersistentLocalId == addressPersistentLocalId) - .ToList(); + .AsNoTracking() + .Where(x => x.AddressPersistentLocalId == addressPersistentLocalId) + .ToList(); foreach (var relation in relations) { diff --git a/src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs b/src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs new file mode 100644 index 00000000..9160403f --- /dev/null +++ b/src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs @@ -0,0 +1,63 @@ +namespace ParcelRegistry.Parcel.Commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Be.Vlaanderen.Basisregisters.Generators.Guid; + using Be.Vlaanderen.Basisregisters.GrAr.Provenance; + using Be.Vlaanderen.Basisregisters.Utilities; + + public class ReaddressAddresses : IHasCommandProvenance + { + private static readonly Guid Namespace = new Guid("646d3ef7-6cbc-4b33-b75f-e5d72e48c356"); + public ParcelId ParcelId { get; } + public IReadOnlyList Addresses { get; } + public Provenance Provenance { get; } + + public ReaddressAddresses( + ParcelId parcelId, + IEnumerable addresses, + Provenance provenance) + { + ParcelId = parcelId; + Addresses = addresses.ToList(); + Provenance = provenance; + } + + public Guid CreateCommandId() + => Deterministic.Create(Namespace, $"ReaddressAddresses-{ToString()}"); + + public override string? ToString() + => ToStringBuilder.ToString(IdentityFields()); + + private IEnumerable IdentityFields() + { + yield return ParcelId; + + foreach (var address in Addresses) + { + yield return address.SourceAddressPersistentLocalId; + yield return address.DestinationAddressPersistentLocalId; + } + + foreach (var field in Provenance.GetIdentityFields()) + { + yield return field; + } + } + } + + public class ReaddressData + { + public AddressPersistentLocalId SourceAddressPersistentLocalId { get; } + public AddressPersistentLocalId DestinationAddressPersistentLocalId { get; } + + public ReaddressData( + AddressPersistentLocalId sourceAddressPersistentLocalId, + AddressPersistentLocalId destinationAddressPersistentLocalId) + { + SourceAddressPersistentLocalId = sourceAddressPersistentLocalId; + DestinationAddressPersistentLocalId = destinationAddressPersistentLocalId; + } + } +} From 633075f9cda6efadf4cb6738d94bdc80b05b7edb Mon Sep 17 00:00:00 2001 From: jvandaal Date: Wed, 15 May 2024 10:50:57 +0200 Subject: [PATCH 02/13] refactor: consume StreetNameWasReaddressed --- .../CommandHandlingKafkaProjection.cs | 52 ++- .../Parcel/Commands/ReaddressAddresses.cs | 8 +- .../CommandHandlingKafkaProjectionTests.cs | 312 +++++++++++++----- 3 files changed, 244 insertions(+), 128 deletions(-) diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 2cc4d822..67fa1d62 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -1,7 +1,6 @@ namespace ParcelRegistry.Consumer.Address.Projections { using System; - using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -175,40 +174,27 @@ await DetachBecauseRemoved( new AddressPersistentLocalId(boxNumberAddress.SourceAddressPersistentLocalId), new AddressPersistentLocalId(boxNumberAddress.DestinationAddressPersistentLocalId)))) .ToList(); - /* - * We krijgen allemaal adres ids: bron en doel adressen - * Met alle bronadressen zoeken we alle unieke percelen op - * Stuur naar ieder perceel alle herkoppelingen - */ - var parcels = new Dictionary>(); - foreach (var readdress in readdresses) - { - var relations = backOfficeContext.ParcelAddressRelations - .AsNoTracking() - .Where(x => - x.AddressPersistentLocalId == readdress.SourceAddressPersistentLocalId) - .ToList(); - - foreach (var parcelAddressRelation in relations) - { - if (parcels.TryGetValue(parcelAddressRelation.ParcelId, out var addresses)) - { - addresses.Add(readdress); - } - else - { - parcels[parcelAddressRelation.ParcelId] = [readdress]; - } - } - } - foreach (var parcel in parcels) - { - var command = new ReaddressAddresses( - new ParcelId(parcel.Key), - parcel.Value, - FromProvenance(message.Provenance)); + var sourceAddressPersistentLocalIds = readdresses + .Select(x => (int)x.SourceAddressPersistentLocalId) + .ToList(); + var parcelAddressRelations = await backOfficeContext.ParcelAddressRelations + .AsNoTracking() + .Where(x => sourceAddressPersistentLocalIds.Contains(x.AddressPersistentLocalId)) + .ToListAsync(cancellationToken: ct); + + var commands = parcelAddressRelations + .GroupBy( + relation => relation.ParcelId, + relation => readdresses.Where(x => x.SourceAddressPersistentLocalId == relation.AddressPersistentLocalId)) + .Select(x => new ReaddressAddresses( + new ParcelId(x.Key), + x.SelectMany(a => a), + FromProvenance(message.Provenance))); + + foreach (var command in commands) + { await commandHandler.Handle(command, ct); } }); diff --git a/src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs b/src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs index 9160403f..edfa3633 100644 --- a/src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs +++ b/src/ParcelRegistry/Parcel/Commands/ReaddressAddresses.cs @@ -11,16 +11,16 @@ public class ReaddressAddresses : IHasCommandProvenance { private static readonly Guid Namespace = new Guid("646d3ef7-6cbc-4b33-b75f-e5d72e48c356"); public ParcelId ParcelId { get; } - public IReadOnlyList Addresses { get; } + public IReadOnlyList Readdresses { get; } public Provenance Provenance { get; } public ReaddressAddresses( ParcelId parcelId, - IEnumerable addresses, + IEnumerable readdresses, Provenance provenance) { ParcelId = parcelId; - Addresses = addresses.ToList(); + Readdresses = readdresses.ToList(); Provenance = provenance; } @@ -34,7 +34,7 @@ private IEnumerable IdentityFields() { yield return ParcelId; - foreach (var address in Addresses) + foreach (var address in Readdresses) { yield return address.SourceAddressPersistentLocalId; yield return address.DestinationAddressPersistentLocalId; diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs index f183d4d7..4477a35a 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs @@ -1,7 +1,7 @@ namespace ParcelRegistry.Tests.ProjectionTests.Consumer.Address { using System; - using System.Collections.Generic; + using System.Linq; using System.Threading; using System.Threading.Tasks; using Api.BackOffice.Abstractions; @@ -32,7 +32,7 @@ public CommandHandlingKafkaProjectionTests(ITestOutputHelper testOutputHelper) : Fixture.Customize(new InfrastructureCustomization()); _mockCommandHandler = new Mock(); - _fakeBackOfficeContext = new FakeBackOfficeContextFactory().CreateDbContext(Array.Empty()); + _fakeBackOfficeContext = new FakeBackOfficeContextFactory().CreateDbContext([]); } [Fact] @@ -63,12 +63,14 @@ public async Task DetachAddressBecauseRemovedAddressWasMigrated() Organisation.Aiv.ToString(), "test")); - AddRelations(addressPersistentLocalId,addressPersistentLocalId); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -101,12 +103,14 @@ public async Task DetachAddressBecauseRejectedAddressWasMigrated() Organisation.Aiv.ToString(), "test")); - AddRelations(addressPersistentLocalId, addressPersistentLocalId); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -139,12 +143,14 @@ public async Task DetachAddressBecauseRetiredAddressWasMigrated() Organisation.Aiv.ToString(), "test")); - AddRelations(addressPersistentLocalId, addressPersistentLocalId); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -179,17 +185,18 @@ public async Task DoNothingWhenAddressStatus(string status) Organisation.Aiv.ToString(), "test")); - AddRelations(addressPersistentLocalId, addressPersistentLocalId); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), - Times.Never); - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), - Times.Never); - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), - Times.Never); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Never); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Never); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Never); await Task.CompletedTask; }); } @@ -197,11 +204,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseAddressWasRemoved() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRemovedV2( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -209,24 +216,26 @@ public async Task DetachAddressBecauseAddressWasRemoved() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); - await Then(async _ => - { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); - await Task.CompletedTask; - }); + await Then(async _ => + { + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + await Task.CompletedTask; + }); } [Fact] public async Task DetachAddressBecauseAddressWasRemovedBecauseHouseNumberWasRemoved() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRemovedBecauseHouseNumberWasRemoved( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -234,24 +243,26 @@ public async Task DetachAddressBecauseAddressWasRemovedBecauseHouseNumberWasRemo Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); - await Then(async _ => - { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); - await Task.CompletedTask; - }); + await Then(async _ => + { + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + await Task.CompletedTask; + }); } [Fact] public async Task DetachAddressBecauseAddressWasRejected() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRejected( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -259,12 +270,14 @@ public async Task DetachAddressBecauseAddressWasRejected() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -272,11 +285,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseHouseNumberWasRejected() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRejectedBecauseHouseNumberWasRejected( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -284,12 +297,14 @@ public async Task DetachAddressBecauseHouseNumberWasRejected() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -297,11 +312,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecause_AddressWasRejectedBecauseHouseNumberWasRejected() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRejectedBecauseHouseNumberWasRejected( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -309,12 +324,14 @@ public async Task DetachAddressBecause_AddressWasRejectedBecauseHouseNumberWasRe Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -322,11 +339,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecause_AddressWasRejectedBecauseHouseNumberWasRetired() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRejectedBecauseHouseNumberWasRetired( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -334,12 +351,14 @@ public async Task DetachAddressBecause_AddressWasRejectedBecauseHouseNumberWasRe Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -347,11 +366,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecause_AddressWasRejectedBecauseStreetNameWasRetired() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRejectedBecauseStreetNameWasRetired( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -359,12 +378,14 @@ public async Task DetachAddressBecause_AddressWasRejectedBecauseStreetNameWasRet Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -372,11 +393,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseAddressWasRetiredV2() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRetiredV2( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -384,12 +405,14 @@ public async Task DetachAddressBecauseAddressWasRetiredV2() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -397,11 +420,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseHouseNumberWasRetired() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRetiredBecauseHouseNumberWasRetired( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -409,12 +432,14 @@ public async Task DetachAddressBecauseHouseNumberWasRetired() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -422,11 +447,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseStreetNameWasRejected() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRetiredBecauseStreetNameWasRejected( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -434,12 +459,14 @@ public async Task DetachAddressBecauseStreetNameWasRejected() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -447,11 +474,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseStreetNameWasRetired() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRetiredBecauseStreetNameWasRetired( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -459,12 +486,14 @@ public async Task DetachAddressBecauseStreetNameWasRetired() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -472,11 +501,11 @@ await Then(async _ => [Fact] public async Task DetachAddressFromBuildingUnitBecauseStreetNameWasRemoved() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRemovedBecauseStreetNameWasRemoved( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -484,12 +513,14 @@ public async Task DetachAddressFromBuildingUnitBecauseStreetNameWasRemoved() Organisation.Aiv.ToString(), "test")); - AddRelations(addressIntId, addressIntId); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -517,7 +548,7 @@ public async Task StreetNameWasReaddressed() "Entry", "ExtendedWkbGeometry", true), - new [] + new[] { new ReaddressedAddressData( sourceBoxNumberAddressPersistentLocalId, @@ -539,8 +570,8 @@ public async Task StreetNameWasReaddressed() Organisation.Aiv.ToString(), "test")); - AddRelations(sourceAddressPersistentLocalId); - AddRelations(sourceBoxNumberAddressPersistentLocalId); + AddParcelAddressRelations(sourceAddressPersistentLocalId); + AddParcelAddressRelations(sourceBoxNumberAddressPersistentLocalId); Given(@event); await Then(async _ => @@ -550,11 +581,11 @@ await Then(async _ => Times.Exactly(2)); _mockCommandHandler.Verify(x => - x.Handle( - It.Is(y => - y.NewAddressPersistentLocalId == destinationAddressPersistentLocalId - && y.PreviousAddressPersistentLocalId == sourceAddressPersistentLocalId), - CancellationToken.None), + x.Handle( + It.Is(y => + y.NewAddressPersistentLocalId == destinationAddressPersistentLocalId + && y.PreviousAddressPersistentLocalId == sourceAddressPersistentLocalId), + CancellationToken.None), Times.Exactly(1)); _mockCommandHandler.Verify(x => x.Handle( @@ -571,11 +602,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseAddressWasRejectedBecauseOfReaddress() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRejectedBecauseOfReaddress( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -583,12 +614,14 @@ public async Task DetachAddressBecauseAddressWasRejectedBecauseOfReaddress() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); await Task.CompletedTask; }); } @@ -596,11 +629,11 @@ await Then(async _ => [Fact] public async Task DetachAddressBecauseAddressWasRetiredBecauseOfReaddress() { - var addressIntId = 456; + var addressPersistentLocalId = 456; var @event = new AddressWasRetiredBecauseOfReaddress( 123, - addressIntId, + addressPersistentLocalId, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), Application.ParcelRegistry.ToString(), @@ -608,23 +641,120 @@ public async Task DetachAddressBecauseAddressWasRetiredBecauseOfReaddress() Organisation.Aiv.ToString(), "test")); - AddRelations(456, 456); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [addressPersistentLocalId]); + + Given(@event); + await Then(async _ => + { + _mockCommandHandler.Verify(x => + x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + await Task.CompletedTask; + }); + } + + [Fact] + public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() + { + var parcelOneId = Fixture.Create(); + var parcelTwoId = Fixture.Create(); + + var sourceAddressPersistentLocalIdOne = 1; + var sourceAddressPersistentLocalIdTwo = 2; + var sourceAddressPersistentLocalIdThree = 5; + var destinationAddressPersistentLocalIdOne = 10; + var destinationAddressPersistentLocalIdTwo = 11; + var destinationAddressPersistentLocalIdThree = 12; + + var parcelOneAddressPersistentLocalIds = new[] { sourceAddressPersistentLocalIdOne, sourceAddressPersistentLocalIdTwo, 3 }; + var parcelTwoAddressPersistentLocalIds = new[] { 4, sourceAddressPersistentLocalIdThree }; + + AddParcelAddressRelations(parcelOneId, parcelOneAddressPersistentLocalIds); + AddParcelAddressRelations(parcelTwoId, parcelTwoAddressPersistentLocalIds); + AddParcelAddressRelations(Fixture.Create(), [6, 7, 8]); + + var @event = new StreetNameWasReaddressed( + Fixture.Create(), + new[] + { + new AddressHouseNumberReaddressedData( + destinationAddressPersistentLocalIdOne, + CreateReaddressedAddressData(sourceAddressPersistentLocalIdOne, destinationAddressPersistentLocalIdOne), + new[] + { + CreateReaddressedAddressData(sourceAddressPersistentLocalIdTwo, destinationAddressPersistentLocalIdTwo) + }), + new AddressHouseNumberReaddressedData( + destinationAddressPersistentLocalIdThree, + CreateReaddressedAddressData(sourceAddressPersistentLocalIdThree, destinationAddressPersistentLocalIdThree), + Array.Empty()), + }, + new Provenance( + Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), + Application.ParcelRegistry.ToString(), + Modification.Update.ToString(), + Organisation.Aiv.ToString(), + "test") + ); Given(@event); await Then(async _ => { - _mockCommandHandler.Verify(x => x.Handle(It.IsAny(), CancellationToken.None), Times.Exactly(2)); + _mockCommandHandler.Verify(x => + x.Handle( + It.Is(y => + y.ParcelId == parcelOneId + && y.Readdresses.Count == 2 + && y.Readdresses.Any(z => + z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdOne + && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdOne) + && y.Readdresses.Any(z => z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdTwo + && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdTwo)), + CancellationToken.None), + Times.Once); + _mockCommandHandler.Verify(x => + x.Handle( + It.Is(y => + y.ParcelId == parcelTwoId + && y.Readdresses.Count == 1 + && y.Readdresses.Any(z => + z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdThree + && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdThree)), + CancellationToken.None), + Times.Once); await Task.CompletedTask; }); } - private void AddRelations(params int[] addressInts) + private ReaddressedAddressData CreateReaddressedAddressData( + int sourceAddressPersistentLocalIdOne, + int destinationAddressPersistentLocalIdOne) + { + return new ReaddressedAddressData( + sourceAddressPersistentLocalIdOne, + destinationAddressPersistentLocalIdOne, + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create()); + } + + private void AddParcelAddressRelations(params int[] addressPersistentLocalIds) + { + AddParcelAddressRelations(Fixture.Create(), addressPersistentLocalIds); + } + + private void AddParcelAddressRelations(ParcelId parcelId, int[] addressPersistentLocalIds) { - foreach (var addressInt in addressInts) + foreach (var addressPersistentLocalId in addressPersistentLocalIds) { _fakeBackOfficeContext.ParcelAddressRelations.Add( - new ParcelAddressRelation(Fixture.Create(), - new AddressPersistentLocalId(addressInt))); + new ParcelAddressRelation(parcelId, new AddressPersistentLocalId(addressPersistentLocalId))); } _fakeBackOfficeContext.SaveChanges(); From f6c2bb948a88334d4b2eab8daaaf370b833275da Mon Sep 17 00:00:00 2001 From: jvandaal Date: Wed, 15 May 2024 10:59:05 +0200 Subject: [PATCH 03/13] chore: comment AddressHouseNumberWasReaddressed event in consumer --- .../CommandHandlingKafkaProjection.cs | 42 ++--- .../CommandHandlingKafkaProjectionTests.cs | 155 +++++++++--------- 2 files changed, 97 insertions(+), 100 deletions(-) diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 67fa1d62..95354b11 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -199,27 +199,27 @@ await DetachBecauseRemoved( } }); - When(async (commandHandler, message, ct) => - { - await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); - - await ReplaceBecauseOfReaddress( - commandHandler, - backOfficeContext, - message.ReaddressedHouseNumber, - message.Provenance, - ct); - - foreach (var readdressedBoxNumber in message.ReaddressedBoxNumbers) - { - await ReplaceBecauseOfReaddress( - commandHandler, - backOfficeContext, - readdressedBoxNumber, - message.Provenance, - ct); - } - }); + // When(async (commandHandler, message, ct) => + // { + // await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); + // + // await ReplaceBecauseOfReaddress( + // commandHandler, + // backOfficeContext, + // message.ReaddressedHouseNumber, + // message.Provenance, + // ct); + // + // foreach (var readdressedBoxNumber in message.ReaddressedBoxNumbers) + // { + // await ReplaceBecauseOfReaddress( + // commandHandler, + // backOfficeContext, + // readdressedBoxNumber, + // message.Provenance, + // ct); + // } + // }); When(async (commandHandler, message, ct) => { diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs index 4477a35a..634fedc1 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs @@ -525,79 +525,79 @@ await Then(async _ => }); } - [Fact] - public async Task StreetNameWasReaddressed() - { - var sourceAddressPersistentLocalId = 1; - var sourceBoxNumberAddressPersistentLocalId = 2; - var destinationAddressPersistentLocalId = 3; - var destinationBoxNumberAddressPersistentLocalId = 4; - - var @event = new AddressHouseNumberWasReaddressed( - 1000000, - sourceAddressPersistentLocalId, - new ReaddressedAddressData( - sourceAddressPersistentLocalId, - destinationAddressPersistentLocalId, - true, - "Current", - "120", - null, - "9000", - "AppointedByAdministrator", - "Entry", - "ExtendedWkbGeometry", - true), - new[] - { - new ReaddressedAddressData( - sourceBoxNumberAddressPersistentLocalId, - destinationBoxNumberAddressPersistentLocalId, - true, - "Current", - "120", - "A", - "9000", - "AppointedByAdministrator", - "Entry", - "ExtendedWkbGeometry", - true), - }, - new Provenance( - Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), - Application.ParcelRegistry.ToString(), - Modification.Update.ToString(), - Organisation.Aiv.ToString(), - "test")); - - AddParcelAddressRelations(sourceAddressPersistentLocalId); - AddParcelAddressRelations(sourceBoxNumberAddressPersistentLocalId); - - Given(@event); - await Then(async _ => - { - _mockCommandHandler.Verify(x => - x.Handle(It.IsAny(), CancellationToken.None), - Times.Exactly(2)); - - _mockCommandHandler.Verify(x => - x.Handle( - It.Is(y => - y.NewAddressPersistentLocalId == destinationAddressPersistentLocalId - && y.PreviousAddressPersistentLocalId == sourceAddressPersistentLocalId), - CancellationToken.None), - Times.Exactly(1)); - _mockCommandHandler.Verify(x => - x.Handle( - It.Is(y => - y.NewAddressPersistentLocalId == destinationBoxNumberAddressPersistentLocalId - && y.PreviousAddressPersistentLocalId == sourceBoxNumberAddressPersistentLocalId), - CancellationToken.None), - Times.Exactly(1)); - - await Task.CompletedTask; - }); - } + // [Fact] + // public async Task StreetNameWasReaddressed() + // { + // var sourceAddressPersistentLocalId = 1; + // var sourceBoxNumberAddressPersistentLocalId = 2; + // var destinationAddressPersistentLocalId = 3; + // var destinationBoxNumberAddressPersistentLocalId = 4; + // + // var @event = new AddressHouseNumberWasReaddressed( + // 1000000, + // sourceAddressPersistentLocalId, + // new ReaddressedAddressData( + // sourceAddressPersistentLocalId, + // destinationAddressPersistentLocalId, + // true, + // "Current", + // "120", + // null, + // "9000", + // "AppointedByAdministrator", + // "Entry", + // "ExtendedWkbGeometry", + // true), + // new[] + // { + // new ReaddressedAddressData( + // sourceBoxNumberAddressPersistentLocalId, + // destinationBoxNumberAddressPersistentLocalId, + // true, + // "Current", + // "120", + // "A", + // "9000", + // "AppointedByAdministrator", + // "Entry", + // "ExtendedWkbGeometry", + // true), + // }, + // new Provenance( + // Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), + // Application.ParcelRegistry.ToString(), + // Modification.Update.ToString(), + // Organisation.Aiv.ToString(), + // "test")); + // + // AddParcelAddressRelations(sourceAddressPersistentLocalId); + // AddParcelAddressRelations(sourceBoxNumberAddressPersistentLocalId); + // + // Given(@event); + // await Then(async _ => + // { + // _mockCommandHandler.Verify(x => + // x.Handle(It.IsAny(), CancellationToken.None), + // Times.Exactly(2)); + // + // _mockCommandHandler.Verify(x => + // x.Handle( + // It.Is(y => + // y.NewAddressPersistentLocalId == destinationAddressPersistentLocalId + // && y.PreviousAddressPersistentLocalId == sourceAddressPersistentLocalId), + // CancellationToken.None), + // Times.Exactly(1)); + // _mockCommandHandler.Verify(x => + // x.Handle( + // It.Is(y => + // y.NewAddressPersistentLocalId == destinationBoxNumberAddressPersistentLocalId + // && y.PreviousAddressPersistentLocalId == sourceBoxNumberAddressPersistentLocalId), + // CancellationToken.None), + // Times.Exactly(1)); + // + // await Task.CompletedTask; + // }); + // } [Fact] public async Task DetachAddressBecauseAddressWasRejectedBecauseOfReaddress() @@ -662,6 +662,7 @@ public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() var sourceAddressPersistentLocalIdOne = 1; var sourceAddressPersistentLocalIdTwo = 2; var sourceAddressPersistentLocalIdThree = 5; + var unattachedSourceAddressPersistentLocalIdOne = 20; var destinationAddressPersistentLocalIdOne = 10; var destinationAddressPersistentLocalIdTwo = 11; var destinationAddressPersistentLocalIdThree = 12; @@ -682,7 +683,8 @@ public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() CreateReaddressedAddressData(sourceAddressPersistentLocalIdOne, destinationAddressPersistentLocalIdOne), new[] { - CreateReaddressedAddressData(sourceAddressPersistentLocalIdTwo, destinationAddressPersistentLocalIdTwo) + CreateReaddressedAddressData(sourceAddressPersistentLocalIdTwo, destinationAddressPersistentLocalIdTwo), + CreateReaddressedAddressData(unattachedSourceAddressPersistentLocalIdOne, 21), }), new AddressHouseNumberReaddressedData( destinationAddressPersistentLocalIdThree, @@ -744,11 +746,6 @@ private ReaddressedAddressData CreateReaddressedAddressData( Fixture.Create()); } - private void AddParcelAddressRelations(params int[] addressPersistentLocalIds) - { - AddParcelAddressRelations(Fixture.Create(), addressPersistentLocalIds); - } - private void AddParcelAddressRelations(ParcelId parcelId, int[] addressPersistentLocalIds) { foreach (var addressPersistentLocalId in addressPersistentLocalIds) From 411e7c5db0d7db4e730f702bfd356823e480d2ae Mon Sep 17 00:00:00 2001 From: Rik De Peuter Date: Wed, 15 May 2024 12:01:30 +0200 Subject: [PATCH 04/13] add domain handling for ReaddressAddresses --- .../CommandHandlingKafkaProjection.cs | 33 +-- .../Parcel/AddressCommandHandlerModule.cs | 22 +- ...WasAttachedBecauseAddressWasReaddressed.cs | 65 ++++++ ...WasDetachedBecauseAddressWasReaddressed.cs | 65 ++++++ .../Parcel/ParcelCommandHandlerModule.cs | 1 - src/ParcelRegistry/Parcel/Parcel_Address.cs | 58 ++++-- .../GivenAddressAttached.cs | 78 ++++++++ .../GivenAddressNotAttached.cs | 188 ++++++++++++++++++ .../Builders/ReaddressAddressesBuilder.cs | 44 ++++ 9 files changed, 504 insertions(+), 50 deletions(-) create mode 100644 src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs create mode 100644 src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs create mode 100644 test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs create mode 100644 test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs create mode 100644 test/ParcelRegistry.Tests/Builders/ReaddressAddressesBuilder.cs diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 95354b11..645d6f95 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -240,38 +240,7 @@ await DetachBecauseRetired( ct); }); } - - private async Task ReplaceBecauseOfReaddress( - CommandHandler commandHandler, - BackOfficeContext backOfficeContext, - ReaddressedAddressData readdressedAddress, - Contracts.Provenance provenance, - CancellationToken ct) - { - var relations = backOfficeContext.ParcelAddressRelations - .AsNoTracking() - .Where(x => - x.AddressPersistentLocalId == readdressedAddress.SourceAddressPersistentLocalId) - .ToList(); - - foreach (var relation in relations) - { - var command = new ReplaceAttachedAddressBecauseAddressWasReaddressed( - new ParcelId(relation.ParcelId), - newAddressPersistentLocalId: new AddressPersistentLocalId(readdressedAddress.DestinationAddressPersistentLocalId), - previousAddressPersistentLocalId: new AddressPersistentLocalId(readdressedAddress.SourceAddressPersistentLocalId), - FromProvenance(provenance)); - - await commandHandler.Handle(command, ct); - - // This should only be handled by the back office projections to prevent conflicts. Else a relation is added or removed twice. - // await backOfficeContext.RemoveIdempotentParcelAddressRelation( - // command.ParcelId, new AddressPersistentLocalId(readdressedAddress.SourceAddressPersistentLocalId), ct); - // await backOfficeContext.AddIdempotentParcelAddressRelation( - // command.ParcelId, new AddressPersistentLocalId(readdressedAddress.DestinationAddressPersistentLocalId), ct); - } - } - + private async Task DetachBecauseRemoved( CommandHandler commandHandler, AddressPersistentLocalId addressPersistentLocalId, diff --git a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs index 02890c65..1c5831f1 100644 --- a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs +++ b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs @@ -82,18 +82,30 @@ public AddressCommandHandlerModule( parcel.DetachAddressBecauseAddressWasRetired(message.Command.AddressPersistentLocalId); }); - For() + //For() + // .AddSqlStreamStore(getStreamStore, getUnitOfWork, eventMapping, eventSerializer, getSnapshotStore) + // .AddEventHash(getUnitOfWork) + // .AddProvenance(getUnitOfWork, provenanceFactory) + // .Handle(async (message, ct) => + // { + // var streamId = new ParcelStreamId(message.Command.ParcelId); + // var parcel = await parcelRepository().GetAsync(streamId, ct); + + // parcel.ReplaceAttachedAddressBecauseAddressWasReaddressed( + // message.Command.NewAddressPersistentLocalId, + // message.Command.PreviousAddressPersistentLocalId); + // }); + + For() .AddSqlStreamStore(getStreamStore, getUnitOfWork, eventMapping, eventSerializer, getSnapshotStore) - .AddEventHash(getUnitOfWork) + .AddEventHash(getUnitOfWork) .AddProvenance(getUnitOfWork, provenanceFactory) .Handle(async (message, ct) => { var streamId = new ParcelStreamId(message.Command.ParcelId); var parcel = await parcelRepository().GetAsync(streamId, ct); - parcel.ReplaceAttachedAddressBecauseAddressWasReaddressed( - message.Command.NewAddressPersistentLocalId, - message.Command.PreviousAddressPersistentLocalId); + parcel.ReplaceAttachedAddressesBecauseAddressesWereReaddressed(message.Command.Readdresses); }); } } diff --git a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs new file mode 100644 index 00000000..e668ee93 --- /dev/null +++ b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs @@ -0,0 +1,65 @@ +namespace ParcelRegistry.Parcel.Events +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Be.Vlaanderen.Basisregisters.EventHandling; + using Be.Vlaanderen.Basisregisters.GrAr.Common; + using Be.Vlaanderen.Basisregisters.GrAr.Provenance; + using Newtonsoft.Json; + + [EventTags(EventTag.For.Sync, EventTag.For.Edit, Tag.Address)] + [EventName(EventName)] + [EventDescription("Het adres werd gekoppeld aan het perceel door heradressering.")] + public sealed class ParcelAddressWasAttachedBecauseAddressWasReaddressed : IParcelEvent + { + public const string EventName = "ParcelAddressWasAttachedBecauseAddressWasReaddressed"; // BE CAREFUL CHANGING THIS!! + + [EventPropertyDescription("Interne GUID van het perceel.")] + public Guid ParcelId { get; } + + [EventPropertyDescription("CaPaKey (= objectidentificator) van het perceel, waarbij forward slashes vervangen zijn door koppeltekens i.f.v. gebruik in URI's.")] + public string CaPaKey { get; } + + [EventPropertyDescription("Objectidentificator van het adres.")] + public int AddressPersistentLocalId { get; } + + [EventPropertyDescription("Metadata bij het event.")] + public ProvenanceData Provenance { get; private set; } + + public ParcelAddressWasAttachedBecauseAddressWasReaddressed( + ParcelId parcelId, + VbrCaPaKey vbrCaPaKey, + AddressPersistentLocalId addressPersistentLocalId) + { + ParcelId = parcelId; + CaPaKey = vbrCaPaKey; + AddressPersistentLocalId = addressPersistentLocalId; + } + + [JsonConstructor] + private ParcelAddressWasAttachedBecauseAddressWasReaddressed( + Guid parcelId, + string caPaKey, + int addressPersistentLocalId, + ProvenanceData provenance) + : this( + new ParcelId(parcelId), + new VbrCaPaKey(caPaKey), + new AddressPersistentLocalId(addressPersistentLocalId)) + => ((ISetProvenance)this).SetProvenance(provenance.ToProvenance()); + + void ISetProvenance.SetProvenance(Provenance provenance) => Provenance = new ProvenanceData(provenance); + + public IEnumerable GetHashFields() + { + var fields = Provenance.GetHashFields().ToList(); + fields.Add(ParcelId.ToString("D")); + fields.Add(CaPaKey); + fields.Add(AddressPersistentLocalId.ToString()); + return fields; + } + + public string GetHash() => this.ToEventHash(EventName); + } +} diff --git a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs new file mode 100644 index 00000000..88f25c68 --- /dev/null +++ b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs @@ -0,0 +1,65 @@ +namespace ParcelRegistry.Parcel.Events +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Be.Vlaanderen.Basisregisters.EventHandling; + using Be.Vlaanderen.Basisregisters.GrAr.Common; + using Be.Vlaanderen.Basisregisters.GrAr.Provenance; + using Newtonsoft.Json; + + [EventTags(EventTag.For.Sync, EventTag.For.Edit, Tag.Address)] + [EventName(EventName)] + [EventDescription("Het adres werd ontkoppeld van het perceel door heradressering.")] + public sealed class ParcelAddressWasDetachedBecauseAddressWasReaddressed : IParcelEvent + { + public const string EventName = "ParcelAddressWasDetachedBecauseAddressWasReaddressed"; // BE CAREFUL CHANGING THIS!! + + [EventPropertyDescription("Interne GUID van het perceel.")] + public Guid ParcelId { get; } + + [EventPropertyDescription("CaPaKey (= objectidentificator) van het perceel, waarbij forward slashes vervangen zijn door koppeltekens i.f.v. gebruik in URI's.")] + public string CaPaKey { get; } + + [EventPropertyDescription("Objectidentificator van het adres.")] + public int AddressPersistentLocalId { get; } + + [EventPropertyDescription("Metadata bij het event.")] + public ProvenanceData Provenance { get; private set; } + + public ParcelAddressWasDetachedBecauseAddressWasReaddressed( + ParcelId parcelId, + VbrCaPaKey vbrCaPaKey, + AddressPersistentLocalId addressPersistentLocalId) + { + ParcelId = parcelId; + CaPaKey = vbrCaPaKey; + AddressPersistentLocalId = addressPersistentLocalId; + } + + [JsonConstructor] + private ParcelAddressWasDetachedBecauseAddressWasReaddressed( + Guid parcelId, + string caPaKey, + int addressPersistentLocalId, + ProvenanceData provenance) + : this( + new ParcelId(parcelId), + new VbrCaPaKey(caPaKey), + new AddressPersistentLocalId(addressPersistentLocalId)) + => ((ISetProvenance)this).SetProvenance(provenance.ToProvenance()); + + void ISetProvenance.SetProvenance(Provenance provenance) => Provenance = new ProvenanceData(provenance); + + public IEnumerable GetHashFields() + { + var fields = Provenance.GetHashFields().ToList(); + fields.Add(ParcelId.ToString("D")); + fields.Add(CaPaKey); + fields.Add(AddressPersistentLocalId.ToString()); + return fields; + } + + public string GetHash() => this.ToEventHash(EventName); + } +} diff --git a/src/ParcelRegistry/Parcel/ParcelCommandHandlerModule.cs b/src/ParcelRegistry/Parcel/ParcelCommandHandlerModule.cs index fdf192f0..cd42267e 100644 --- a/src/ParcelRegistry/Parcel/ParcelCommandHandlerModule.cs +++ b/src/ParcelRegistry/Parcel/ParcelCommandHandlerModule.cs @@ -10,7 +10,6 @@ namespace ParcelRegistry.Parcel using Be.Vlaanderen.Basisregisters.GrAr.Provenance; using Commands; using Exceptions; - using Legacy.Commands; using SqlStreamStore; public sealed class ParcelCommandHandlerModule : CommandHandlerModule diff --git a/src/ParcelRegistry/Parcel/Parcel_Address.cs b/src/ParcelRegistry/Parcel/Parcel_Address.cs index 0f2f0d3a..dc6130ba 100644 --- a/src/ParcelRegistry/Parcel/Parcel_Address.cs +++ b/src/ParcelRegistry/Parcel/Parcel_Address.cs @@ -1,6 +1,8 @@ -namespace ParcelRegistry.Parcel +namespace ParcelRegistry.Parcel { + using System.Collections.Generic; using System.Linq; + using Commands; using DataStructures; using Events; using Exceptions; @@ -85,21 +87,53 @@ public void DetachAddressBecauseAddressWasRetired(AddressPersistentLocalId addre ApplyChange(new ParcelAddressWasDetachedBecauseAddressWasRetired(ParcelId, CaPaKey, addressPersistentLocalId)); } - public void ReplaceAttachedAddressBecauseAddressWasReaddressed( - AddressPersistentLocalId addressPersistentLocalId, - AddressPersistentLocalId previousAddressPersistentLocalId) + //public void ReplaceAttachedAddressBecauseAddressWasReaddressed( + // AddressPersistentLocalId addressPersistentLocalId, + // AddressPersistentLocalId previousAddressPersistentLocalId) + //{ + // if (AddressPersistentLocalIds.Contains(addressPersistentLocalId) + // && !AddressPersistentLocalIds.Contains(previousAddressPersistentLocalId)) + // { + // return; + // } + + // ApplyChange(new ParcelAddressWasReplacedBecauseAddressWasReaddressed( + // ParcelId, + // CaPaKey, + // addressPersistentLocalId, + // previousAddressPersistentLocalId)); + //} + + public void ReplaceAttachedAddressesBecauseAddressesWereReaddressed( + IReadOnlyList readdresses) { - if (AddressPersistentLocalIds.Contains(addressPersistentLocalId) - && !AddressPersistentLocalIds.Contains(previousAddressPersistentLocalId)) + var addressPersistentLocalIdsToAttach = readdresses + .Select(x => x.DestinationAddressPersistentLocalId) + .Except(readdresses.Select(x => x.SourceAddressPersistentLocalId)) + .Except(AddressPersistentLocalIds) + .ToList(); + var addressPersistentLocalIdsToDetach = readdresses + .Select(x => x.SourceAddressPersistentLocalId) + .Except(readdresses.Select(x => x.DestinationAddressPersistentLocalId)) + .Where(AddressPersistentLocalIds.Contains) + .ToList(); + // replace event: de readdresses waarvan hun Source of DestinationAddressId in geen ander readdress wordt gebruikt? + + foreach (var addressPersistentLocalId in addressPersistentLocalIdsToDetach) { - return; + ApplyChange(new ParcelAddressWasDetachedBecauseAddressWasReaddressed( + ParcelId, + CaPaKey, + addressPersistentLocalId)); } - ApplyChange(new ParcelAddressWasReplacedBecauseAddressWasReaddressed( - ParcelId, - CaPaKey, - addressPersistentLocalId, - previousAddressPersistentLocalId)); + foreach (var addressPersistentLocalId in addressPersistentLocalIdsToAttach) + { + ApplyChange(new ParcelAddressWasAttachedBecauseAddressWasReaddressed( + ParcelId, + CaPaKey, + addressPersistentLocalId)); + } } } } diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs new file mode 100644 index 00000000..2c1a20a4 --- /dev/null +++ b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs @@ -0,0 +1,78 @@ +namespace ParcelRegistry.Tests.AggregateTests.WhenReaddressingAddresses +{ + using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; + using Builders; + using Fixtures; + using Parcel; + using Parcel.Events; + using Xunit; + using Xunit.Abstractions; + + public class GivenAddressAttached : ParcelRegistryTest + { + public GivenAddressAttached(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + Fixture.Customize(new WithFixedParcelId()); + Fixture.Customize(new WithParcelStatus()); + Fixture.Customize(new Legacy.AutoFixture.WithFixedParcelId()); + } + + [Fact] + public void WithSourceAddressAttached_ThenAttachAndDetach() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(sourceAddressPersistentLocalId) + .WithAddress(2) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .Then( + new ParcelStreamId(command.ParcelId), + new ParcelAddressWasDetachedBecauseAddressWasReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + sourceAddressPersistentLocalId), + new ParcelAddressWasAttachedBecauseAddressWasReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + destinationAddressPersistentLocalId) + )); + } + + [Fact] + public void WithPreviousAddressNotAttached_ThenNothing() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(2) + .WithAddress(destinationAddressPersistentLocalId) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .ThenNone()); + } + } +} diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs new file mode 100644 index 00000000..e23400fe --- /dev/null +++ b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs @@ -0,0 +1,188 @@ +namespace ParcelRegistry.Tests.AggregateTests.WhenReaddressingAddresses +{ + using System.Collections.Generic; + using System.Linq; + using Autofac; + using Be.Vlaanderen.Basisregisters.AggregateSource; + using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting; + using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; + using Builders; + using Fixtures; + using FluentAssertions; + using Parcel; + using Parcel.Events; + using Xunit; + using Xunit.Abstractions; + + public class GivenAddressNotAttached : ParcelRegistryTest + { + public GivenAddressNotAttached(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + Fixture.Customize(new WithFixedParcelId()); + Fixture.Customize(new WithParcelStatus()); + Fixture.Customize(new Legacy.AutoFixture.WithFixedParcelId()); + } + + [Fact] + public void WithPreviousAddressAttached_ThenParcelAddressWasReplacedBecauseAddressWasReaddressed() + { + //TODO-rik fix unit tests + var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); + var addressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithNewAddress(addressPersistentLocalId) + .WithPreviousAddress(previousAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(previousAddressPersistentLocalId) + .WithAddress(2) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .Then(new Fact(new ParcelStreamId(command.ParcelId), + new ParcelAddressWasReplacedBecauseAddressWasReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + addressPersistentLocalId, + previousAddressPersistentLocalId)))); + } + + [Fact] + public void WithPreviousAddressNotAttached_ThenParcelAddressWasReplacedBecauseAddressWasReaddressed() + { + var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); + var addressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithNewAddress(addressPersistentLocalId) + .WithPreviousAddress(previousAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithParcelId(command.ParcelId) + .WithStatus(ParcelStatus.Realized) + .WithAddress(2) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .Then(new Fact(new ParcelStreamId(command.ParcelId), + new ParcelAddressWasReplacedBecauseAddressWasReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + addressPersistentLocalId, + previousAddressPersistentLocalId)))); + } + + [Fact] + public void StateCheck_OnlyPreviousWasAttached() + { + var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); + var newAddressPersistentLocalId = new AddressPersistentLocalId(2); + var otherAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(previousAddressPersistentLocalId) + .WithAddress(otherAddressPersistentLocalId) + .Build(); + + var @event = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) + .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) + .WithNewAddress(newAddressPersistentLocalId) + .WithPreviousAddress(previousAddressPersistentLocalId) + .Build(); + + // Act + var sut = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + sut.Initialize(new List { parcelWasMigrated, @event }); + + // Assert + sut.AddressPersistentLocalIds.Should().HaveCount(2); + sut.AddressPersistentLocalIds.Should().Contain(newAddressPersistentLocalId); + sut.AddressPersistentLocalIds.Should().Contain(otherAddressPersistentLocalId); + sut.AddressPersistentLocalIds.Should().NotContain(previousAddressPersistentLocalId); + sut.LastEventHash.Should().Be(@event.GetHash()); + } + + [Fact] + public void StateCheck_BothPreviousAndNewWereAlreadyAttached() + { + var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); + var newAddressPersistentLocalId = new AddressPersistentLocalId(2); + var otherAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(newAddressPersistentLocalId) + .WithAddress(previousAddressPersistentLocalId) + .WithAddress(otherAddressPersistentLocalId) + .Build(); + + var @event = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) + .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) + .WithNewAddress(newAddressPersistentLocalId) + .WithPreviousAddress(previousAddressPersistentLocalId) + .Build(); + + // Act + var sut = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + sut.Initialize(new List { parcelWasMigrated, @event }); + + // Assert + sut.AddressPersistentLocalIds.Should().HaveCount(3); + sut.AddressPersistentLocalIds.Where(x => x == newAddressPersistentLocalId).Should().HaveCount(2); + sut.AddressPersistentLocalIds.Should().Contain(otherAddressPersistentLocalId); + sut.AddressPersistentLocalIds.Should().NotContain(previousAddressPersistentLocalId); + sut.LastEventHash.Should().Be(@event.GetHash()); + } + + [Fact] + public void StateCheck_BothPreviousAndNewWereAlreadyAttached_ReaddressTwice() + { + var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); + var newAddressPersistentLocalId = new AddressPersistentLocalId(2); + var otherAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(newAddressPersistentLocalId) + .WithAddress(previousAddressPersistentLocalId) + .WithAddress(otherAddressPersistentLocalId) + .Build(); + + var firstEvent = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) + .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) + .WithNewAddress(newAddressPersistentLocalId) + .WithPreviousAddress(previousAddressPersistentLocalId) + .Build(); + + var secondEvent = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) + .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) + .WithNewAddress(previousAddressPersistentLocalId) + .WithPreviousAddress(newAddressPersistentLocalId) + .Build(); + + // Act + var sut = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + sut.Initialize(new List { parcelWasMigrated, firstEvent, secondEvent }); + + // Assert + sut.AddressPersistentLocalIds.Should().HaveCount(3); + sut.AddressPersistentLocalIds.Where(x => x == newAddressPersistentLocalId).Should().HaveCount(1); + sut.AddressPersistentLocalIds.Where(x => x == previousAddressPersistentLocalId).Should().HaveCount(1); + sut.AddressPersistentLocalIds.Should().Contain(otherAddressPersistentLocalId); + sut.LastEventHash.Should().Be(secondEvent.GetHash()); + } + } +} diff --git a/test/ParcelRegistry.Tests/Builders/ReaddressAddressesBuilder.cs b/test/ParcelRegistry.Tests/Builders/ReaddressAddressesBuilder.cs new file mode 100644 index 00000000..30c9e2b0 --- /dev/null +++ b/test/ParcelRegistry.Tests/Builders/ReaddressAddressesBuilder.cs @@ -0,0 +1,44 @@ +namespace ParcelRegistry.Tests.Builders +{ + using System.Collections.Generic; + using AutoFixture; + using Be.Vlaanderen.Basisregisters.GrAr.Provenance; + using Parcel; + using Parcel.Commands; + + public class ReaddressAddressesBuilder + { + private readonly Fixture _fixture; + private ParcelId? _parcelId; + private readonly List _readdresses = []; + + public ReaddressAddressesBuilder(Fixture fixture) + { + _fixture = fixture; + } + + public ReaddressAddressesBuilder WithParcelId(ParcelId parcelId) + { + _parcelId = parcelId; + + return this; + } + + public ReaddressAddressesBuilder WithReaddress(int sourceAddressPersistentLocalId, int destinationAddressPersistentLocalId) + { + _readdresses.Add(new ReaddressData( + new AddressPersistentLocalId(sourceAddressPersistentLocalId), + new AddressPersistentLocalId(destinationAddressPersistentLocalId))); + + return this; + } + + public ReaddressAddresses Build() + { + return new ReaddressAddresses( + _parcelId ?? _fixture.Create(), + _readdresses, + _fixture.Create()); + } + } +} From 78b52bfaf4ef4adb569a7c401148d827999ecfb4 Mon Sep 17 00:00:00 2001 From: jvandaal Date: Wed, 15 May 2024 15:10:22 +0200 Subject: [PATCH 05/13] feat: ParcelAddressesWereReaddressed --- .../CommandHandlingKafkaProjection.cs | 7 +- .../Parcel/AddressCommandHandlerModule.cs | 2 +- ...WasAttachedBecauseAddressWasReaddressed.cs | 65 ------ ...WasDetachedBecauseAddressWasReaddressed.cs | 65 ------ .../Events/ParcelAddressesWereReaddressed.cs | 102 ++++++++ src/ParcelRegistry/Parcel/Parcel_Address.cs | 26 +-- src/ParcelRegistry/Parcel/Parcel_State.cs | 16 ++ .../GivenAddressAttached.cs | 78 ------- .../GivenAddressNotAttached.cs | 188 --------------- .../GivenParcelExists.cs | 221 ++++++++++++++++++ .../CommandHandlingKafkaProjectionTests.cs | 5 +- 11 files changed, 357 insertions(+), 418 deletions(-) delete mode 100644 src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs delete mode 100644 src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs create mode 100644 src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs delete mode 100644 test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs delete mode 100644 test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs create mode 100644 test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenParcelExists.cs diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 645d6f95..85daae6c 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -184,7 +184,7 @@ await DetachBecauseRemoved( .Where(x => sourceAddressPersistentLocalIds.Contains(x.AddressPersistentLocalId)) .ToListAsync(cancellationToken: ct); - var commands = parcelAddressRelations + var commandByParcels = parcelAddressRelations .GroupBy( relation => relation.ParcelId, relation => readdresses.Where(x => x.SourceAddressPersistentLocalId == relation.AddressPersistentLocalId)) @@ -193,7 +193,7 @@ await DetachBecauseRemoved( x.SelectMany(a => a), FromProvenance(message.Provenance))); - foreach (var command in commands) + foreach (var command in commandByParcels) { await commandHandler.Handle(command, ct); } @@ -230,7 +230,6 @@ await DetachBecauseRejected( ct); }); - When(async (commandHandler, message, ct) => { await DetachBecauseRetired( @@ -240,7 +239,7 @@ await DetachBecauseRetired( ct); }); } - + private async Task DetachBecauseRemoved( CommandHandler commandHandler, AddressPersistentLocalId addressPersistentLocalId, diff --git a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs index 1c5831f1..94760474 100644 --- a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs +++ b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs @@ -105,7 +105,7 @@ public AddressCommandHandlerModule( var streamId = new ParcelStreamId(message.Command.ParcelId); var parcel = await parcelRepository().GetAsync(streamId, ct); - parcel.ReplaceAttachedAddressesBecauseAddressesWereReaddressed(message.Command.Readdresses); + parcel.ReaddressAddresses(message.Command.Readdresses); }); } } diff --git a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs deleted file mode 100644 index e668ee93..00000000 --- a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasAttachedBecauseAddressWasReaddressed.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace ParcelRegistry.Parcel.Events -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Be.Vlaanderen.Basisregisters.EventHandling; - using Be.Vlaanderen.Basisregisters.GrAr.Common; - using Be.Vlaanderen.Basisregisters.GrAr.Provenance; - using Newtonsoft.Json; - - [EventTags(EventTag.For.Sync, EventTag.For.Edit, Tag.Address)] - [EventName(EventName)] - [EventDescription("Het adres werd gekoppeld aan het perceel door heradressering.")] - public sealed class ParcelAddressWasAttachedBecauseAddressWasReaddressed : IParcelEvent - { - public const string EventName = "ParcelAddressWasAttachedBecauseAddressWasReaddressed"; // BE CAREFUL CHANGING THIS!! - - [EventPropertyDescription("Interne GUID van het perceel.")] - public Guid ParcelId { get; } - - [EventPropertyDescription("CaPaKey (= objectidentificator) van het perceel, waarbij forward slashes vervangen zijn door koppeltekens i.f.v. gebruik in URI's.")] - public string CaPaKey { get; } - - [EventPropertyDescription("Objectidentificator van het adres.")] - public int AddressPersistentLocalId { get; } - - [EventPropertyDescription("Metadata bij het event.")] - public ProvenanceData Provenance { get; private set; } - - public ParcelAddressWasAttachedBecauseAddressWasReaddressed( - ParcelId parcelId, - VbrCaPaKey vbrCaPaKey, - AddressPersistentLocalId addressPersistentLocalId) - { - ParcelId = parcelId; - CaPaKey = vbrCaPaKey; - AddressPersistentLocalId = addressPersistentLocalId; - } - - [JsonConstructor] - private ParcelAddressWasAttachedBecauseAddressWasReaddressed( - Guid parcelId, - string caPaKey, - int addressPersistentLocalId, - ProvenanceData provenance) - : this( - new ParcelId(parcelId), - new VbrCaPaKey(caPaKey), - new AddressPersistentLocalId(addressPersistentLocalId)) - => ((ISetProvenance)this).SetProvenance(provenance.ToProvenance()); - - void ISetProvenance.SetProvenance(Provenance provenance) => Provenance = new ProvenanceData(provenance); - - public IEnumerable GetHashFields() - { - var fields = Provenance.GetHashFields().ToList(); - fields.Add(ParcelId.ToString("D")); - fields.Add(CaPaKey); - fields.Add(AddressPersistentLocalId.ToString()); - return fields; - } - - public string GetHash() => this.ToEventHash(EventName); - } -} diff --git a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs deleted file mode 100644 index 88f25c68..00000000 --- a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasDetachedBecauseAddressWasReaddressed.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace ParcelRegistry.Parcel.Events -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Be.Vlaanderen.Basisregisters.EventHandling; - using Be.Vlaanderen.Basisregisters.GrAr.Common; - using Be.Vlaanderen.Basisregisters.GrAr.Provenance; - using Newtonsoft.Json; - - [EventTags(EventTag.For.Sync, EventTag.For.Edit, Tag.Address)] - [EventName(EventName)] - [EventDescription("Het adres werd ontkoppeld van het perceel door heradressering.")] - public sealed class ParcelAddressWasDetachedBecauseAddressWasReaddressed : IParcelEvent - { - public const string EventName = "ParcelAddressWasDetachedBecauseAddressWasReaddressed"; // BE CAREFUL CHANGING THIS!! - - [EventPropertyDescription("Interne GUID van het perceel.")] - public Guid ParcelId { get; } - - [EventPropertyDescription("CaPaKey (= objectidentificator) van het perceel, waarbij forward slashes vervangen zijn door koppeltekens i.f.v. gebruik in URI's.")] - public string CaPaKey { get; } - - [EventPropertyDescription("Objectidentificator van het adres.")] - public int AddressPersistentLocalId { get; } - - [EventPropertyDescription("Metadata bij het event.")] - public ProvenanceData Provenance { get; private set; } - - public ParcelAddressWasDetachedBecauseAddressWasReaddressed( - ParcelId parcelId, - VbrCaPaKey vbrCaPaKey, - AddressPersistentLocalId addressPersistentLocalId) - { - ParcelId = parcelId; - CaPaKey = vbrCaPaKey; - AddressPersistentLocalId = addressPersistentLocalId; - } - - [JsonConstructor] - private ParcelAddressWasDetachedBecauseAddressWasReaddressed( - Guid parcelId, - string caPaKey, - int addressPersistentLocalId, - ProvenanceData provenance) - : this( - new ParcelId(parcelId), - new VbrCaPaKey(caPaKey), - new AddressPersistentLocalId(addressPersistentLocalId)) - => ((ISetProvenance)this).SetProvenance(provenance.ToProvenance()); - - void ISetProvenance.SetProvenance(Provenance provenance) => Provenance = new ProvenanceData(provenance); - - public IEnumerable GetHashFields() - { - var fields = Provenance.GetHashFields().ToList(); - fields.Add(ParcelId.ToString("D")); - fields.Add(CaPaKey); - fields.Add(AddressPersistentLocalId.ToString()); - return fields; - } - - public string GetHash() => this.ToEventHash(EventName); - } -} diff --git a/src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs new file mode 100644 index 00000000..9ab83833 --- /dev/null +++ b/src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs @@ -0,0 +1,102 @@ +namespace ParcelRegistry.Parcel.Events +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Be.Vlaanderen.Basisregisters.EventHandling; + using Be.Vlaanderen.Basisregisters.GrAr.Common; + using Be.Vlaanderen.Basisregisters.GrAr.Provenance; + using Commands; + using Newtonsoft.Json; + + [EventTags(EventTag.For.Sync, EventTag.For.Edit, Tag.Address)] + [EventName(EventName)] + [EventDescription("De adresherkoppelingen op het perceel door heradressering.")] + public sealed class ParcelAddressesWereReaddressed : IParcelEvent + { + public const string EventName = "ParcelAddressesWereReaddressed"; // BE CAREFUL CHANGING THIS!! + + [EventPropertyDescription("Interne GUID van het perceel.")] + public Guid ParcelId { get; } + + [EventPropertyDescription("CaPaKey (= objectidentificator) van het perceel, waarbij forward slashes vervangen zijn door koppeltekens i.f.v. gebruik in URI's.")] + public string CaPaKey { get; } + + [EventPropertyDescription("Objectidentificatoren van nieuw gekoppelde adressen.")] + public IEnumerable AttachedAddressPersistentLocalIds { get; } + + [EventPropertyDescription("Objectidentificatoren van ontkoppelde adressen.")] + public IEnumerable DetachedAddressPersistentLocalIds { get; } + + [EventPropertyDescription("De geheradresseerde adressen uit het Adressenregister.")] + public IEnumerable AddressRegistryReaddresses { get; } + + [EventPropertyDescription("Metadata bij het event.")] + public ProvenanceData Provenance { get; private set; } + + public ParcelAddressesWereReaddressed( + ParcelId parcelId, + VbrCaPaKey vbrCaPaKey, + IEnumerable attachedAddressPersistentLocalIds, + IEnumerable detachedAddressPersistentLocalIds, + IEnumerable addressRegistryReaddresses) + { + ParcelId = parcelId; + CaPaKey = vbrCaPaKey; + AttachedAddressPersistentLocalIds = attachedAddressPersistentLocalIds.Select(x => (int)x).ToList(); + DetachedAddressPersistentLocalIds = detachedAddressPersistentLocalIds.Select(x => (int)x).ToList(); + AddressRegistryReaddresses = addressRegistryReaddresses; + } + + [JsonConstructor] + public ParcelAddressesWereReaddressed( + Guid parcelId, + string caPaKey, + IEnumerable attachedAddressPersistentLocalIds, + IEnumerable detachedAddressPersistentLocalIds, + IEnumerable addressRegistryReaddresses, + ProvenanceData provenance) + : this( + new ParcelId(parcelId), + new VbrCaPaKey(caPaKey), + attachedAddressPersistentLocalIds.Select(x => new AddressPersistentLocalId(x)), + detachedAddressPersistentLocalIds.Select(x => new AddressPersistentLocalId(x)), + addressRegistryReaddresses) + => ((ISetProvenance)this).SetProvenance(provenance.ToProvenance()); + + void ISetProvenance.SetProvenance(Provenance provenance) => Provenance = new ProvenanceData(provenance); + + public IEnumerable GetHashFields() + { + var fields = Provenance.GetHashFields().ToList(); + fields.Add(ParcelId.ToString("D")); + fields.Add(CaPaKey); + fields.AddRange(AttachedAddressPersistentLocalIds.Select(addressPersistentLocalId => addressPersistentLocalId.ToString())); + fields.AddRange(DetachedAddressPersistentLocalIds.Select(addressPersistentLocalId => addressPersistentLocalId.ToString())); + + return fields; + } + + public string GetHash() => this.ToEventHash(EventName); + } + + public class AddressRegistryReaddress + { + public int SourceAddressPersistentLocalId { get; } + public int DestinationAddressPersistentLocalId { get; } + + public AddressRegistryReaddress( + ReaddressData readdressData) + : this((int)readdressData.SourceAddressPersistentLocalId, readdressData.DestinationAddressPersistentLocalId) + { } + + [JsonConstructor] + private AddressRegistryReaddress( + int sourceAddressPersistentLocalId, + int destinationAddressPersistentLocalId) + { + SourceAddressPersistentLocalId = sourceAddressPersistentLocalId; + DestinationAddressPersistentLocalId = destinationAddressPersistentLocalId; + } + } +} diff --git a/src/ParcelRegistry/Parcel/Parcel_Address.cs b/src/ParcelRegistry/Parcel/Parcel_Address.cs index dc6130ba..31268cb5 100644 --- a/src/ParcelRegistry/Parcel/Parcel_Address.cs +++ b/src/ParcelRegistry/Parcel/Parcel_Address.cs @@ -104,7 +104,7 @@ public void DetachAddressBecauseAddressWasRetired(AddressPersistentLocalId addre // previousAddressPersistentLocalId)); //} - public void ReplaceAttachedAddressesBecauseAddressesWereReaddressed( + public void ReaddressAddresses( IReadOnlyList readdresses) { var addressPersistentLocalIdsToAttach = readdresses @@ -112,28 +112,24 @@ public void ReplaceAttachedAddressesBecauseAddressesWereReaddressed( .Except(readdresses.Select(x => x.SourceAddressPersistentLocalId)) .Except(AddressPersistentLocalIds) .ToList(); + var addressPersistentLocalIdsToDetach = readdresses .Select(x => x.SourceAddressPersistentLocalId) .Except(readdresses.Select(x => x.DestinationAddressPersistentLocalId)) .Where(AddressPersistentLocalIds.Contains) .ToList(); - // replace event: de readdresses waarvan hun Source of DestinationAddressId in geen ander readdress wordt gebruikt? - - foreach (var addressPersistentLocalId in addressPersistentLocalIdsToDetach) - { - ApplyChange(new ParcelAddressWasDetachedBecauseAddressWasReaddressed( - ParcelId, - CaPaKey, - addressPersistentLocalId)); - } - foreach (var addressPersistentLocalId in addressPersistentLocalIdsToAttach) + if (!addressPersistentLocalIdsToAttach.Any() && !addressPersistentLocalIdsToDetach.Any()) { - ApplyChange(new ParcelAddressWasAttachedBecauseAddressWasReaddressed( - ParcelId, - CaPaKey, - addressPersistentLocalId)); + return; } + + ApplyChange(new ParcelAddressesWereReaddressed( + ParcelId, + CaPaKey, + addressPersistentLocalIdsToAttach, + addressPersistentLocalIdsToDetach, + readdresses.Select(x => new AddressRegistryReaddress(x)))); } } } diff --git a/src/ParcelRegistry/Parcel/Parcel_State.cs b/src/ParcelRegistry/Parcel/Parcel_State.cs index 93f26254..86e3093d 100644 --- a/src/ParcelRegistry/Parcel/Parcel_State.cs +++ b/src/ParcelRegistry/Parcel/Parcel_State.cs @@ -48,6 +48,7 @@ private Parcel() Register(When); Register(When); Register(When); + Register(When); Register(When); } @@ -146,6 +147,21 @@ private void When(ParcelAddressWasReplacedBecauseAddressWasReaddressed @event) _lastEvent = @event; } + private void When(ParcelAddressesWereReaddressed @event) + { + foreach (var addressPersistentLocalId in @event.DetachedAddressPersistentLocalIds) + { + _addressPersistentLocalIds.Remove(new AddressPersistentLocalId(addressPersistentLocalId)); + } + + foreach (var addressPersistentLocalId in @event.AttachedAddressPersistentLocalIds) + { + _addressPersistentLocalIds.Add(new AddressPersistentLocalId(addressPersistentLocalId)); + } + + _lastEvent = @event; + } + private void When(ParcelSnapshotV2 @event) { ParcelId = new ParcelId(@event.ParcelId); diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs deleted file mode 100644 index 2c1a20a4..00000000 --- a/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressAttached.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace ParcelRegistry.Tests.AggregateTests.WhenReaddressingAddresses -{ - using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; - using Builders; - using Fixtures; - using Parcel; - using Parcel.Events; - using Xunit; - using Xunit.Abstractions; - - public class GivenAddressAttached : ParcelRegistryTest - { - public GivenAddressAttached(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - Fixture.Customize(new WithFixedParcelId()); - Fixture.Customize(new WithParcelStatus()); - Fixture.Customize(new Legacy.AutoFixture.WithFixedParcelId()); - } - - [Fact] - public void WithSourceAddressAttached_ThenAttachAndDetach() - { - var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); - var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReaddressAddressesBuilder(Fixture) - .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(sourceAddressPersistentLocalId) - .WithAddress(2) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .Then( - new ParcelStreamId(command.ParcelId), - new ParcelAddressWasDetachedBecauseAddressWasReaddressed( - command.ParcelId, - new VbrCaPaKey(parcelWasMigrated.CaPaKey), - sourceAddressPersistentLocalId), - new ParcelAddressWasAttachedBecauseAddressWasReaddressed( - command.ParcelId, - new VbrCaPaKey(parcelWasMigrated.CaPaKey), - destinationAddressPersistentLocalId) - )); - } - - [Fact] - public void WithPreviousAddressNotAttached_ThenNothing() - { - var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); - var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReaddressAddressesBuilder(Fixture) - .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(2) - .WithAddress(destinationAddressPersistentLocalId) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .ThenNone()); - } - } -} diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs deleted file mode 100644 index e23400fe..00000000 --- a/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenAddressNotAttached.cs +++ /dev/null @@ -1,188 +0,0 @@ -namespace ParcelRegistry.Tests.AggregateTests.WhenReaddressingAddresses -{ - using System.Collections.Generic; - using System.Linq; - using Autofac; - using Be.Vlaanderen.Basisregisters.AggregateSource; - using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting; - using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; - using Builders; - using Fixtures; - using FluentAssertions; - using Parcel; - using Parcel.Events; - using Xunit; - using Xunit.Abstractions; - - public class GivenAddressNotAttached : ParcelRegistryTest - { - public GivenAddressNotAttached(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - Fixture.Customize(new WithFixedParcelId()); - Fixture.Customize(new WithParcelStatus()); - Fixture.Customize(new Legacy.AutoFixture.WithFixedParcelId()); - } - - [Fact] - public void WithPreviousAddressAttached_ThenParcelAddressWasReplacedBecauseAddressWasReaddressed() - { - //TODO-rik fix unit tests - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var addressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReaddressAddressesBuilder(Fixture) - .WithNewAddress(addressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(previousAddressPersistentLocalId) - .WithAddress(2) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .Then(new Fact(new ParcelStreamId(command.ParcelId), - new ParcelAddressWasReplacedBecauseAddressWasReaddressed( - command.ParcelId, - new VbrCaPaKey(parcelWasMigrated.CaPaKey), - addressPersistentLocalId, - previousAddressPersistentLocalId)))); - } - - [Fact] - public void WithPreviousAddressNotAttached_ThenParcelAddressWasReplacedBecauseAddressWasReaddressed() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var addressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReaddressAddressesBuilder(Fixture) - .WithNewAddress(addressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithParcelId(command.ParcelId) - .WithStatus(ParcelStatus.Realized) - .WithAddress(2) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .Then(new Fact(new ParcelStreamId(command.ParcelId), - new ParcelAddressWasReplacedBecauseAddressWasReaddressed( - command.ParcelId, - new VbrCaPaKey(parcelWasMigrated.CaPaKey), - addressPersistentLocalId, - previousAddressPersistentLocalId)))); - } - - [Fact] - public void StateCheck_OnlyPreviousWasAttached() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var newAddressPersistentLocalId = new AddressPersistentLocalId(2); - var otherAddressPersistentLocalId = new AddressPersistentLocalId(3); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(previousAddressPersistentLocalId) - .WithAddress(otherAddressPersistentLocalId) - .Build(); - - var @event = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) - .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) - .WithNewAddress(newAddressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - // Act - var sut = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); - sut.Initialize(new List { parcelWasMigrated, @event }); - - // Assert - sut.AddressPersistentLocalIds.Should().HaveCount(2); - sut.AddressPersistentLocalIds.Should().Contain(newAddressPersistentLocalId); - sut.AddressPersistentLocalIds.Should().Contain(otherAddressPersistentLocalId); - sut.AddressPersistentLocalIds.Should().NotContain(previousAddressPersistentLocalId); - sut.LastEventHash.Should().Be(@event.GetHash()); - } - - [Fact] - public void StateCheck_BothPreviousAndNewWereAlreadyAttached() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var newAddressPersistentLocalId = new AddressPersistentLocalId(2); - var otherAddressPersistentLocalId = new AddressPersistentLocalId(3); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(newAddressPersistentLocalId) - .WithAddress(previousAddressPersistentLocalId) - .WithAddress(otherAddressPersistentLocalId) - .Build(); - - var @event = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) - .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) - .WithNewAddress(newAddressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - // Act - var sut = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); - sut.Initialize(new List { parcelWasMigrated, @event }); - - // Assert - sut.AddressPersistentLocalIds.Should().HaveCount(3); - sut.AddressPersistentLocalIds.Where(x => x == newAddressPersistentLocalId).Should().HaveCount(2); - sut.AddressPersistentLocalIds.Should().Contain(otherAddressPersistentLocalId); - sut.AddressPersistentLocalIds.Should().NotContain(previousAddressPersistentLocalId); - sut.LastEventHash.Should().Be(@event.GetHash()); - } - - [Fact] - public void StateCheck_BothPreviousAndNewWereAlreadyAttached_ReaddressTwice() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var newAddressPersistentLocalId = new AddressPersistentLocalId(2); - var otherAddressPersistentLocalId = new AddressPersistentLocalId(3); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(newAddressPersistentLocalId) - .WithAddress(previousAddressPersistentLocalId) - .WithAddress(otherAddressPersistentLocalId) - .Build(); - - var firstEvent = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) - .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) - .WithNewAddress(newAddressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - var secondEvent = new ParcelAddressWasReplacedBecauseAddressWasReaddressedBuilder(Fixture) - .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) - .WithNewAddress(previousAddressPersistentLocalId) - .WithPreviousAddress(newAddressPersistentLocalId) - .Build(); - - // Act - var sut = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); - sut.Initialize(new List { parcelWasMigrated, firstEvent, secondEvent }); - - // Assert - sut.AddressPersistentLocalIds.Should().HaveCount(3); - sut.AddressPersistentLocalIds.Where(x => x == newAddressPersistentLocalId).Should().HaveCount(1); - sut.AddressPersistentLocalIds.Where(x => x == previousAddressPersistentLocalId).Should().HaveCount(1); - sut.AddressPersistentLocalIds.Should().Contain(otherAddressPersistentLocalId); - sut.LastEventHash.Should().Be(secondEvent.GetHash()); - } - } -} diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenParcelExists.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenParcelExists.cs new file mode 100644 index 00000000..d1b6e5f4 --- /dev/null +++ b/test/ParcelRegistry.Tests/AggregateTests/WhenReaddressingAddresses/GivenParcelExists.cs @@ -0,0 +1,221 @@ +namespace ParcelRegistry.Tests.AggregateTests.WhenReaddressingAddresses +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Autofac; + using AutoFixture; + using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting; + using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; + using Builders; + using EventExtensions; + using Fixtures; + using FluentAssertions; + using Parcel; + using Parcel.Commands; + using Parcel.Events; + using Xunit; + using Xunit.Abstractions; + + public class GivenParcelExists : ParcelRegistryTest + { + public GivenParcelExists(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + Fixture.Customize(new WithFixedParcelId()); + Fixture.Customize(new WithParcelStatus()); + Fixture.Customize(new Legacy.AutoFixture.WithFixedParcelId()); + } + + [Fact] + public void WithSourceAddressAttachedAndDestinationAddressNotAttached_ThenAttachAndDetach() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(sourceAddressPersistentLocalId) + .WithAddress(2) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .Then( + new ParcelStreamId(command.ParcelId), + new ParcelAddressesWereReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + new[] { destinationAddressPersistentLocalId }, + new[] { sourceAddressPersistentLocalId }, + command.Readdresses.Select(x => new AddressRegistryReaddress(x)).ToList() + ) + )); + } + + [Fact] + public void WithSourceAddressAttachedAndDestinationAddressAlreadyAttached_ThenOnlyDetach() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(2) + .WithAddress(sourceAddressPersistentLocalId) + .WithAddress(destinationAddressPersistentLocalId) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .Then( + new ParcelStreamId(command.ParcelId), + new ParcelAddressesWereReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + Array.Empty(), + new[] { sourceAddressPersistentLocalId }, + command.Readdresses.Select(x => new AddressRegistryReaddress(x)).ToList() + ) + )); + } + + [Fact] + public void WithSourceAddressNotAttachedAndDestinationNotAttached_ThenOnlyAttach() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(2) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .Then( + new ParcelStreamId(command.ParcelId), + new ParcelAddressesWereReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + new[] { destinationAddressPersistentLocalId }, + Array.Empty(), + command.Readdresses.Select(x => new AddressRegistryReaddress(x)).ToList() + ) + )); + } + + [Fact] + public void WithSourceAddressNotAttachedAndDestinationAddressAlreadyAttached_ThenNothing() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var destinationAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithReaddress(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(2) + .WithAddress(destinationAddressPersistentLocalId) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .ThenNone()); + } + + [Fact] + public void WithTwoReaddressesAndAddressIsBothSourceAndDestination_ThenOneAttachAndOneDetach() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var firstAddressPersistentLocalId = new AddressPersistentLocalId(3); + var secondAddressPersistentLocalId = new AddressPersistentLocalId(5); + + var command = new ReaddressAddressesBuilder(Fixture) + .WithReaddress(sourceAddressPersistentLocalId, firstAddressPersistentLocalId) + .WithReaddress(secondAddressPersistentLocalId, sourceAddressPersistentLocalId) + .Build(); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(secondAddressPersistentLocalId) + .WithAddress(sourceAddressPersistentLocalId) + .Build(); + + Assert(new Scenario() + .Given( + new ParcelStreamId(command.ParcelId), + parcelWasMigrated) + .When(command) + .Then( + new ParcelStreamId(command.ParcelId), + new ParcelAddressesWereReaddressed( + command.ParcelId, + new VbrCaPaKey(parcelWasMigrated.CaPaKey), + new[] { firstAddressPersistentLocalId }, + new[] { secondAddressPersistentLocalId }, + command.Readdresses.Select(x => new AddressRegistryReaddress(x)).ToList() + ) + )); + } + + [Fact] + public void StateCheck() + { + var sourceAddressPersistentLocalId = new AddressPersistentLocalId(1); + var destinationAddressPersistentLocalId = new AddressPersistentLocalId(2); + var otherAddressPersistentLocalId = new AddressPersistentLocalId(3); + + var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) + .WithStatus(ParcelStatus.Realized) + .WithAddress(sourceAddressPersistentLocalId) + .WithAddress(otherAddressPersistentLocalId) + .Build(); + + var @event = new ParcelAddressesWereReaddressed( + Fixture.Create(), + Fixture.Create(), + new[] { destinationAddressPersistentLocalId }, + new[] { sourceAddressPersistentLocalId }, + new []{ new AddressRegistryReaddress(new ReaddressData(sourceAddressPersistentLocalId, destinationAddressPersistentLocalId)) } + ); + @event.SetFixtureProvenance(Fixture); + + // Act + var sut = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + sut.Initialize(new List { parcelWasMigrated, @event }); + + // Assert + sut.AddressPersistentLocalIds.Should().HaveCount(2); + sut.AddressPersistentLocalIds.Should().Contain(destinationAddressPersistentLocalId); + sut.AddressPersistentLocalIds.Should().Contain(otherAddressPersistentLocalId); + sut.AddressPersistentLocalIds.Should().NotContain(sourceAddressPersistentLocalId); + sut.LastEventHash.Should().Be(@event.GetHash()); + } + } +} diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs index 634fedc1..df9960b7 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs @@ -710,8 +710,9 @@ await Then(async _ => && y.Readdresses.Any(z => z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdOne && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdOne) - && y.Readdresses.Any(z => z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdTwo - && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdTwo)), + && y.Readdresses.Any(z => + z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdTwo + && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdTwo)), CancellationToken.None), Times.Once); _mockCommandHandler.Verify(x => From 243a21be778c3c29b17520f7c759611a3fafced0 Mon Sep 17 00:00:00 2001 From: jvandaal Date: Wed, 15 May 2024 16:24:54 +0200 Subject: [PATCH 06/13] chore: remove ReplaceAttachedAddressBecauseAddressWasReaddressed --- .../CommandHandlingKafkaProjection.cs | 22 ------ .../Parcel/AddressCommandHandlerModule.cs | 14 ---- ...chedAddressBecauseAddressWasReaddressed.cs | 50 ------------ src/ParcelRegistry/Parcel/Parcel_Address.cs | 17 ---- .../GivenAddressAttached.cs | 77 ------------------- ...essNotAttached.cs => GivenParcelExists.cs} | 63 --------------- ...ressBecauseAddressWasReaddressedBuilder.cs | 50 ------------ 7 files changed, 293 deletions(-) delete mode 100644 src/ParcelRegistry/Parcel/Commands/ReplaceAttachedAddressBecauseAddressWasReaddressed.cs delete mode 100644 test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenAddressAttached.cs rename test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/{GivenAddressNotAttached.cs => GivenParcelExists.cs} (68%) delete mode 100644 test/ParcelRegistry.Tests/Builders/ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder.cs diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 85daae6c..fd56ff8e 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -199,28 +199,6 @@ await DetachBecauseRemoved( } }); - // When(async (commandHandler, message, ct) => - // { - // await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); - // - // await ReplaceBecauseOfReaddress( - // commandHandler, - // backOfficeContext, - // message.ReaddressedHouseNumber, - // message.Provenance, - // ct); - // - // foreach (var readdressedBoxNumber in message.ReaddressedBoxNumbers) - // { - // await ReplaceBecauseOfReaddress( - // commandHandler, - // backOfficeContext, - // readdressedBoxNumber, - // message.Provenance, - // ct); - // } - // }); - When(async (commandHandler, message, ct) => { await DetachBecauseRejected( diff --git a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs index 94760474..c0c9f85d 100644 --- a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs +++ b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs @@ -82,20 +82,6 @@ public AddressCommandHandlerModule( parcel.DetachAddressBecauseAddressWasRetired(message.Command.AddressPersistentLocalId); }); - //For() - // .AddSqlStreamStore(getStreamStore, getUnitOfWork, eventMapping, eventSerializer, getSnapshotStore) - // .AddEventHash(getUnitOfWork) - // .AddProvenance(getUnitOfWork, provenanceFactory) - // .Handle(async (message, ct) => - // { - // var streamId = new ParcelStreamId(message.Command.ParcelId); - // var parcel = await parcelRepository().GetAsync(streamId, ct); - - // parcel.ReplaceAttachedAddressBecauseAddressWasReaddressed( - // message.Command.NewAddressPersistentLocalId, - // message.Command.PreviousAddressPersistentLocalId); - // }); - For() .AddSqlStreamStore(getStreamStore, getUnitOfWork, eventMapping, eventSerializer, getSnapshotStore) .AddEventHash(getUnitOfWork) diff --git a/src/ParcelRegistry/Parcel/Commands/ReplaceAttachedAddressBecauseAddressWasReaddressed.cs b/src/ParcelRegistry/Parcel/Commands/ReplaceAttachedAddressBecauseAddressWasReaddressed.cs deleted file mode 100644 index 9686f0b1..00000000 --- a/src/ParcelRegistry/Parcel/Commands/ReplaceAttachedAddressBecauseAddressWasReaddressed.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace ParcelRegistry.Parcel.Commands -{ - using System; - using System.Collections.Generic; - using Be.Vlaanderen.Basisregisters.Generators.Guid; - using Be.Vlaanderen.Basisregisters.GrAr.Provenance; - using Be.Vlaanderen.Basisregisters.Utilities; - using ParcelRegistry.Parcel; - - public sealed class ReplaceAttachedAddressBecauseAddressWasReaddressed : IHasCommandProvenance - { - private static readonly Guid Namespace = new Guid("35b8a5cf-df02-4350-b7f7-f9ebc506dfcb"); - - public ParcelId ParcelId { get; } - public AddressPersistentLocalId NewAddressPersistentLocalId { get; } - public AddressPersistentLocalId PreviousAddressPersistentLocalId { get; } - - public Provenance Provenance { get; } - - public ReplaceAttachedAddressBecauseAddressWasReaddressed( - ParcelId parcelId, - AddressPersistentLocalId newAddressPersistentLocalId, - AddressPersistentLocalId previousAddressPersistentLocalId, - Provenance provenance) - { - ParcelId = parcelId; - NewAddressPersistentLocalId = newAddressPersistentLocalId; - PreviousAddressPersistentLocalId = previousAddressPersistentLocalId; - Provenance = provenance; - } - - public Guid CreateCommandId() - => Deterministic.Create(Namespace, $"ReplaceAttachedAddressBecauseAddressWasReaddressed-{ToString()}"); - - public override string? ToString() - => ToStringBuilder.ToString(IdentityFields()); - - private IEnumerable IdentityFields() - { - yield return ParcelId; - yield return NewAddressPersistentLocalId; - yield return PreviousAddressPersistentLocalId; - - foreach (var field in Provenance.GetIdentityFields()) - { - yield return field; - } - } - } -} diff --git a/src/ParcelRegistry/Parcel/Parcel_Address.cs b/src/ParcelRegistry/Parcel/Parcel_Address.cs index 31268cb5..398aee8e 100644 --- a/src/ParcelRegistry/Parcel/Parcel_Address.cs +++ b/src/ParcelRegistry/Parcel/Parcel_Address.cs @@ -87,23 +87,6 @@ public void DetachAddressBecauseAddressWasRetired(AddressPersistentLocalId addre ApplyChange(new ParcelAddressWasDetachedBecauseAddressWasRetired(ParcelId, CaPaKey, addressPersistentLocalId)); } - //public void ReplaceAttachedAddressBecauseAddressWasReaddressed( - // AddressPersistentLocalId addressPersistentLocalId, - // AddressPersistentLocalId previousAddressPersistentLocalId) - //{ - // if (AddressPersistentLocalIds.Contains(addressPersistentLocalId) - // && !AddressPersistentLocalIds.Contains(previousAddressPersistentLocalId)) - // { - // return; - // } - - // ApplyChange(new ParcelAddressWasReplacedBecauseAddressWasReaddressed( - // ParcelId, - // CaPaKey, - // addressPersistentLocalId, - // previousAddressPersistentLocalId)); - //} - public void ReaddressAddresses( IReadOnlyList readdresses) { diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenAddressAttached.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenAddressAttached.cs deleted file mode 100644 index 71c3c269..00000000 --- a/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenAddressAttached.cs +++ /dev/null @@ -1,77 +0,0 @@ -namespace ParcelRegistry.Tests.AggregateTests.WhenReplacingAttachedAddressBecauseAddressWasReaddressed -{ - using Be.Vlaanderen.Basisregisters.AggregateSource; - using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; - using Builders; - using Fixtures; - using Parcel; - using Parcel.Events; - using Xunit; - using Xunit.Abstractions; - - public class GivenAddressAttached : ParcelRegistryTest - { - public GivenAddressAttached(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - Fixture.Customize(new WithFixedParcelId()); - Fixture.Customize(new WithParcelStatus()); - Fixture.Customize(new Legacy.AutoFixture.WithFixedParcelId()); - } - - [Fact] - public void WithPreviousAddressAttached_ThenParcelAddressWasReplacedBecauseAddressWasReaddressed() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var addressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder(Fixture) - .WithNewAddress(addressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(previousAddressPersistentLocalId) - .WithAddress(2) - .WithAddress(addressPersistentLocalId) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .Then(new Fact(new ParcelStreamId(command.ParcelId), - new ParcelAddressWasReplacedBecauseAddressWasReaddressed( - command.ParcelId, - new VbrCaPaKey(parcelWasMigrated.CaPaKey), - addressPersistentLocalId, - previousAddressPersistentLocalId)))); - } - - [Fact] - public void WithPreviousAddressNotAttached_ThenNothing() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var addressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder(Fixture) - .WithNewAddress(addressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(2) - .WithAddress(addressPersistentLocalId) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .ThenNone()); - } - } -} diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenAddressNotAttached.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenParcelExists.cs similarity index 68% rename from test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenAddressNotAttached.cs rename to test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenParcelExists.cs index 7a160f25..a189c38d 100644 --- a/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenAddressNotAttached.cs +++ b/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAttachedAddressBecauseAddressWasReaddressed/GivenParcelExists.cs @@ -3,14 +3,11 @@ namespace ParcelRegistry.Tests.AggregateTests.WhenReplacingAttachedAddressBecaus using System.Collections.Generic; using System.Linq; using Autofac; - using Be.Vlaanderen.Basisregisters.AggregateSource; using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting; - using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; using Builders; using Fixtures; using FluentAssertions; using Parcel; - using Parcel.Events; using Xunit; using Xunit.Abstractions; @@ -23,66 +20,6 @@ public GivenAddressNotAttached(ITestOutputHelper testOutputHelper) : base(testOu Fixture.Customize(new Legacy.AutoFixture.WithFixedParcelId()); } - [Fact] - public void WithPreviousAddressAttached_ThenParcelAddressWasReplacedBecauseAddressWasReaddressed() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var addressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder(Fixture) - .WithNewAddress(addressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithStatus(ParcelStatus.Realized) - .WithAddress(previousAddressPersistentLocalId) - .WithAddress(2) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .Then(new Fact(new ParcelStreamId(command.ParcelId), - new ParcelAddressWasReplacedBecauseAddressWasReaddressed( - command.ParcelId, - new VbrCaPaKey(parcelWasMigrated.CaPaKey), - addressPersistentLocalId, - previousAddressPersistentLocalId)))); - } - - [Fact] - public void WithPreviousAddressNotAttached_ThenParcelAddressWasReplacedBecauseAddressWasReaddressed() - { - var previousAddressPersistentLocalId = new AddressPersistentLocalId(1); - var addressPersistentLocalId = new AddressPersistentLocalId(3); - - var command = new ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder(Fixture) - .WithNewAddress(addressPersistentLocalId) - .WithPreviousAddress(previousAddressPersistentLocalId) - .Build(); - - var parcelWasMigrated = new ParcelWasMigratedBuilder(Fixture) - .WithParcelId(command.ParcelId) - .WithStatus(ParcelStatus.Realized) - .WithAddress(2) - .Build(); - - Assert(new Scenario() - .Given( - new ParcelStreamId(command.ParcelId), - parcelWasMigrated) - .When(command) - .Then(new Fact(new ParcelStreamId(command.ParcelId), - new ParcelAddressWasReplacedBecauseAddressWasReaddressed( - command.ParcelId, - new VbrCaPaKey(parcelWasMigrated.CaPaKey), - addressPersistentLocalId, - previousAddressPersistentLocalId)))); - } - [Fact] public void StateCheck_OnlyPreviousWasAttached() { diff --git a/test/ParcelRegistry.Tests/Builders/ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder.cs b/test/ParcelRegistry.Tests/Builders/ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder.cs deleted file mode 100644 index 73f03f80..00000000 --- a/test/ParcelRegistry.Tests/Builders/ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace ParcelRegistry.Tests.Builders -{ - using AutoFixture; - using Be.Vlaanderen.Basisregisters.GrAr.Provenance; - using Parcel; - using Parcel.Commands; - - public class ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder - { - private readonly Fixture _fixture; - private ParcelId? _parcelId; - private AddressPersistentLocalId? _newAddressPersistentLocalId; - private AddressPersistentLocalId? _previousAddressPersistentLocalId; - - public ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder(Fixture fixture) - { - _fixture = fixture; - } - - public ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder WithParcelId(ParcelId parcelId) - { - _parcelId = parcelId; - - return this; - } - - public ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder WithNewAddress(int address) - { - _newAddressPersistentLocalId = new AddressPersistentLocalId(address); - - return this; - } - - public ReplaceAttachedAddressBecauseAddressWasReaddressedBuilder WithPreviousAddress(int address) - { - _previousAddressPersistentLocalId = new AddressPersistentLocalId(address); - - return this; - } - - public ReplaceAttachedAddressBecauseAddressWasReaddressed Build() - { - return new ReplaceAttachedAddressBecauseAddressWasReaddressed( - _parcelId ?? _fixture.Create(), - _newAddressPersistentLocalId ?? _fixture.Create(), - _previousAddressPersistentLocalId ?? _fixture.Create(), - _fixture.Create()); - } - } -} From faaacfc797fa1e8dc442537974291fb8456b3eb9 Mon Sep 17 00:00:00 2001 From: jvandaal Date: Wed, 15 May 2024 16:53:21 +0200 Subject: [PATCH 07/13] feat: ParcelAddressesWereReaddressed projections --- .../ProducerProjections.cs | 14 +++++++ .../BackOfficeProjections.cs | 37 ++++++++++++++++++ .../ParcelLinkExtractProjections.cs | 30 +++++++++++++- .../ParcelLatestItemProjections.cs | 27 +++++++++++++ .../ParcelVersion/ParcelVersionProjections.cs | 32 +++++++++++++++ .../LastChangedListProjections.cs | 5 +++ .../ParcelDetailV2Projections.cs | 39 +++++++++++++++++++ .../ParcelSyndicationProjections.cs | 21 +++++++++- .../Infrastructure/Modules/ApiModule.cs | 4 +- 9 files changed, 203 insertions(+), 6 deletions(-) diff --git a/src/ParcelRegistry.Producer.Snapshot.Oslo/ProducerProjections.cs b/src/ParcelRegistry.Producer.Snapshot.Oslo/ProducerProjections.cs index 3057fc3c..60414eb9 100644 --- a/src/ParcelRegistry.Producer.Snapshot.Oslo/ProducerProjections.cs +++ b/src/ParcelRegistry.Producer.Snapshot.Oslo/ProducerProjections.cs @@ -120,6 +120,20 @@ await snapshotManager.FindMatchingSnapshot( ct); }); + When>(async (_, message, ct) => + { + await FindAndProduce(async () => + await snapshotManager.FindMatchingSnapshot( + message.Message.CaPaKey, + message.Message.Provenance.Timestamp, + message.Message.GetHash(), + message.Position, + throwStaleWhenGone: false, + ct), + message.Position, + ct); + }); + When>(async (_, message, ct) => { await FindAndProduce(async () => diff --git a/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs b/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs index 73890a48..b2ff47e9 100644 --- a/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs +++ b/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs @@ -106,6 +106,43 @@ await backOfficeContext.AddIdempotentParcelAddressRelation( newAddress.Count += 1; } }); + + When>(async (_, message, cancellationToken) => + { + await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken); + + foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) + { + var relation = await backOfficeContext.FindParcelAddressRelation( + new ParcelId(message.Message.ParcelId), + new AddressPersistentLocalId(addressPersistentLocalId), + cancellationToken); + + if (relation is not null) + { + await backOfficeContext.RemoveIdempotentParcelAddressRelation( + new ParcelId(message.Message.ParcelId), + new AddressPersistentLocalId(addressPersistentLocalId), + cancellationToken); + } + } + + foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) + { + var relation = await backOfficeContext.FindParcelAddressRelation( + new ParcelId(message.Message.ParcelId), + new AddressPersistentLocalId(addressPersistentLocalId), + cancellationToken); + + if (relation is null) + { + await backOfficeContext.AddIdempotentParcelAddressRelation( + new ParcelId(message.Message.ParcelId), + new AddressPersistentLocalId(addressPersistentLocalId), + cancellationToken); + } + } + }); } } } diff --git a/src/ParcelRegistry.Projections.Extract/ParcelLinkExtractWithCount/ParcelLinkExtractProjections.cs b/src/ParcelRegistry.Projections.Extract/ParcelLinkExtractWithCount/ParcelLinkExtractProjections.cs index 0b2c57c7..9f369c25 100644 --- a/src/ParcelRegistry.Projections.Extract/ParcelLinkExtractWithCount/ParcelLinkExtractProjections.cs +++ b/src/ParcelRegistry.Projections.Extract/ParcelLinkExtractWithCount/ParcelLinkExtractProjections.cs @@ -7,7 +7,6 @@ namespace ParcelRegistry.Projections.Extract.ParcelLinkExtractWithCount using Be.Vlaanderen.Basisregisters.GrAr.Extracts; using Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; - using Microsoft.Extensions.Options; using Parcel.Events; [ConnectedProjectionName("Extract perceelkoppelingen met adres")] @@ -18,7 +17,7 @@ public sealed class ParcelLinkExtractProjections : ConnectedProjection extractConfig, Encoding encoding) + public ParcelLinkExtractProjections(Encoding encoding) { _encoding = encoding ?? throw new ArgumentNullException(nameof(encoding)); @@ -130,6 +129,33 @@ await context newAddress.Count += 1; } }); + + When>(async (context, message, ct) => + { + foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) + { + var relation = await context + .ParcelLinkExtractWithCount + .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); + + if (relation is not null) + { + context.ParcelLinkExtractWithCount.Remove(relation); + } + } + + foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) + { + var relation = await context + .ParcelLinkExtractWithCount + .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); + + if (relation is not null) + { + await context.ParcelLinkExtractWithCount.AddAsync(relation, ct); + } + } + }); } private static async Task RemoveParcelLink( diff --git a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs index ab53af92..53c5dbcd 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs @@ -137,6 +137,33 @@ await context } }); + When>(async (context, message, ct) => + { + foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) + { + var relation = await context + .ParcelLatestItemAddresses + .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); + + if (relation is not null) + { + context.ParcelLatestItemAddresses.Remove(relation); + } + } + + foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) + { + var relation = await context + .ParcelLatestItemAddresses + .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); + + if (relation is not null) + { + await context.ParcelLatestItemAddresses.AddAsync(relation, ct); + } + } + }); + When>(async (context, message, ct) => { var latestItemAddress = await context diff --git a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs index 85ad4e5c..3564a8b8 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs @@ -160,6 +160,38 @@ await context } }); + When>(async (context, message, ct) => + { + await context.CreateNewParcelVersion( + message.Message.ParcelId, + message, + _ => { }, ct); + + foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) + { + var relation = await context + .ParcelVersionAddresses + .FindAsync([message.Position, message.Message.ParcelId, addressPersistentLocalId], ct); + + if (relation is not null) + { + context.ParcelVersionAddresses.Remove(relation); + } + } + + foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) + { + var relation = await context + .ParcelVersionAddresses + .FindAsync([message.Position, message.Message.ParcelId, addressPersistentLocalId], ct); + + if (relation is not null) + { + await context.ParcelVersionAddresses.AddAsync(relation, ct); + } + } + }); + When>(async (context, message, ct) => { await context.CreateNewParcelVersion( diff --git a/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs b/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs index f5cf0ccd..959af620 100644 --- a/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs +++ b/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs @@ -114,6 +114,11 @@ public LastChangedListProjections(ICacheValidator cacheValidator) await GetLastChangedRecordsAndUpdatePosition(message.Message.ParcelId.ToString(), message.Position, context, ct); }); + When>(async (context, message, ct) => + { + await GetLastChangedRecordsAndUpdatePosition(message.Message.ParcelId.ToString(), message.Position, context, ct); + }); + When>(async (context, message, ct) => { var records = await GetLastChangedRecordsAndUpdatePosition(message.Message.ParcelId.ToString(), message.Position, context, ct); diff --git a/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs b/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs index bc1b1ca6..877108fa 100644 --- a/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs +++ b/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs @@ -211,6 +211,45 @@ await context.FindAndUpdateParcelDetail( ct); }); + When>(async (context, message, ct) => + { + await context.FindAndUpdateParcelDetail( + message.Message.ParcelId, + entity => + { + context.Entry(entity).Collection(x => x.Addresses).Load(); + + + foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) + { + var relation = entity.Addresses.SingleOrDefault(parcelAddress => + parcelAddress.AddressPersistentLocalId == addressPersistentLocalId + && parcelAddress.ParcelId == message.Message.ParcelId); + + if (relation is not null) + { + entity.Addresses.Remove(relation); + } + } + + foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) + { + var relation = entity.Addresses.SingleOrDefault(parcelAddress => + parcelAddress.AddressPersistentLocalId == addressPersistentLocalId + && parcelAddress.ParcelId == message.Message.ParcelId); + + if (relation is not null) + { + entity.Addresses.Add(new ParcelDetailAddressV2(message.Message.ParcelId, addressPersistentLocalId)); + } + } + + UpdateHash(entity, message); + UpdateVersionTimestamp(entity, message.Message.Provenance.Timestamp); + }, + ct); + }); + When>(async (context, message, ct) => { var (geometryType, gml) = ToGml(message.Message.ExtendedWkbGeometry); diff --git a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs index c1c5ac8a..ae2dc595 100755 --- a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs +++ b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs @@ -5,7 +5,6 @@ namespace ParcelRegistry.Projections.Legacy.ParcelSyndication using Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; using Be.Vlaanderen.Basisregisters.Utilities.HexByteConvertor; - using NetTopologySuite.IO; using Parcel; using Parcel.Events; using ParcelRegistry.Legacy.Events; @@ -273,6 +272,26 @@ await context.CreateNewParcelSyndicationItem( }, ct); }); + + When>(async (context, message, ct) => + { + await context.CreateNewParcelSyndicationItem( + message.Message.ParcelId, + message, + x => + { + foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) + { + x.RemoveAddressPersistentLocalId(addressPersistentLocalId); + } + + foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) + { + x.AddAddressPersistentLocalId(addressPersistentLocalId); + } + }, + ct); + }); } private static async Task DoNothing() diff --git a/src/ParcelRegistry.Projector/Infrastructure/Modules/ApiModule.cs b/src/ParcelRegistry.Projector/Infrastructure/Modules/ApiModule.cs index 84aa01c7..f87ea993 100644 --- a/src/ParcelRegistry.Projector/Infrastructure/Modules/ApiModule.cs +++ b/src/ParcelRegistry.Projector/Infrastructure/Modules/ApiModule.cs @@ -102,9 +102,7 @@ private void RegisterExtractV2Projections(ContainerBuilder builder) DbaseCodePage.Western_European_ANSI.ToEncoding()), ConnectedProjectionSettings.Default) .RegisterProjections( - context => new ParcelLinkExtractWithCountProjections( - context.Resolve>(), - DbaseCodePage.Western_European_ANSI.ToEncoding()), + context => new ParcelLinkExtractWithCountProjections(DbaseCodePage.Western_European_ANSI.ToEncoding()), ConnectedProjectionSettings.Default); } From 355b643a9bc78310bab66f5c272c5277e64f5736 Mon Sep 17 00:00:00 2001 From: jvandaal Date: Thu, 16 May 2024 11:28:14 +0200 Subject: [PATCH 08/13] test: projections tests --- .../ParcelLatestItemProjections.cs | 9 +- .../ParcelVersion/ParcelVersionProjections.cs | 10 +- .../ParcelDetailV2Projections.cs | 2 +- .../Events/ParcelAddressesWereReaddressed.cs | 13 ++- .../ParcelAddressesWereReaddressedBuilder.cs | 58 ++++++++++++ .../Fixtures/WithUniqueInteger.cs | 23 +++++ .../InfrastructureEventsTests.cs | 9 +- ...celBackOfficeProjectionsTests-Readdress.cs | 49 ++++++++++ ...rcelLatestItemProjectionTests-Readdress.cs | 64 +++++++++++++ .../ParcelVersionProjectionTests-Readdress.cs | 94 +++++++++++++++++++ .../ParcelDetailItemV2Tests-Readdress.cs | 84 +++++++++++++++++ .../Legacy/ParcelSyndicationV2Tests.cs | 88 ++++++++++++++++- 12 files changed, 490 insertions(+), 13 deletions(-) create mode 100644 test/ParcelRegistry.Tests/Builders/ParcelAddressesWereReaddressedBuilder.cs create mode 100644 test/ParcelRegistry.Tests/Fixtures/WithUniqueInteger.cs diff --git a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs index 53c5dbcd..bc5faccf 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs @@ -157,9 +157,14 @@ await context .ParcelLatestItemAddresses .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); - if (relation is not null) + if (relation is null) { - await context.ParcelLatestItemAddresses.AddAsync(relation, ct); + await context.ParcelLatestItemAddresses.AddAsync( + new ParcelLatestItemAddress( + message.Message.ParcelId, + addressPersistentLocalId, + message.Message.CaPaKey), + ct); } } }); diff --git a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs index 3564a8b8..f75d995b 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs @@ -185,9 +185,15 @@ await context.CreateNewParcelVersion( .ParcelVersionAddresses .FindAsync([message.Position, message.Message.ParcelId, addressPersistentLocalId], ct); - if (relation is not null) + if (relation is null) { - await context.ParcelVersionAddresses.AddAsync(relation, ct); + await context.ParcelVersionAddresses.AddAsync( + new ParcelVersionAddress( + message.Position, + message.Message.ParcelId, + addressPersistentLocalId, + message.Message.CaPaKey), + ct); } } }); diff --git a/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs b/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs index 877108fa..d16c4a2d 100644 --- a/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs +++ b/src/ParcelRegistry.Projections.Legacy/ParcelDetailWithCountV2/ParcelDetailV2Projections.cs @@ -238,7 +238,7 @@ await context.FindAndUpdateParcelDetail( parcelAddress.AddressPersistentLocalId == addressPersistentLocalId && parcelAddress.ParcelId == message.Message.ParcelId); - if (relation is not null) + if (relation is null) { entity.Addresses.Add(new ParcelDetailAddressV2(message.Message.ParcelId, addressPersistentLocalId)); } diff --git a/src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs index 9ab83833..6e91c7a8 100644 --- a/src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs +++ b/src/ParcelRegistry/Parcel/Events/ParcelAddressesWereReaddressed.cs @@ -49,7 +49,7 @@ public ParcelAddressesWereReaddressed( } [JsonConstructor] - public ParcelAddressesWereReaddressed( + private ParcelAddressesWereReaddressed( Guid parcelId, string caPaKey, IEnumerable attachedAddressPersistentLocalIds, @@ -80,15 +80,20 @@ public IEnumerable GetHashFields() public string GetHash() => this.ToEventHash(EventName); } - public class AddressRegistryReaddress + public sealed class AddressRegistryReaddress { + [EventPropertyDescription("Objectidentificator van het bronadres.")] public int SourceAddressPersistentLocalId { get; } + + [EventPropertyDescription("Objectidentificator van het doeladres.")] public int DestinationAddressPersistentLocalId { get; } public AddressRegistryReaddress( ReaddressData readdressData) - : this((int)readdressData.SourceAddressPersistentLocalId, readdressData.DestinationAddressPersistentLocalId) - { } + { + SourceAddressPersistentLocalId = readdressData.SourceAddressPersistentLocalId; + DestinationAddressPersistentLocalId = readdressData.DestinationAddressPersistentLocalId; + } [JsonConstructor] private AddressRegistryReaddress( diff --git a/test/ParcelRegistry.Tests/Builders/ParcelAddressesWereReaddressedBuilder.cs b/test/ParcelRegistry.Tests/Builders/ParcelAddressesWereReaddressedBuilder.cs new file mode 100644 index 00000000..29c9b944 --- /dev/null +++ b/test/ParcelRegistry.Tests/Builders/ParcelAddressesWereReaddressedBuilder.cs @@ -0,0 +1,58 @@ +namespace ParcelRegistry.Tests.Builders +{ + using System.Collections.Generic; + using AutoFixture; + using EventExtensions; + using Parcel; + using Parcel.Commands; + using Parcel.Events; + + public class ParcelAddressesWereReaddressedBuilder(Fixture fixture) + { + private readonly List _attachedAddressPersistentLocalIds = []; + private readonly List _detachedAddressPersistentLocalIds = []; + private readonly List _addressRegistryReaddresses = []; + + public ParcelAddressesWereReaddressedBuilder WithAttachedAddress(int addressPersistentLocalid) + { + _attachedAddressPersistentLocalIds.Add(new AddressPersistentLocalId(addressPersistentLocalid)); + return this; + } + + public ParcelAddressesWereReaddressedBuilder WithDetachedAddress(int addressPersistentLocalid) + { + _detachedAddressPersistentLocalIds.Add(new AddressPersistentLocalId(addressPersistentLocalid)); + return this; + } + + public ParcelAddressesWereReaddressedBuilder WithReaddress( + int sourceAddressPersistentLocalId, + int destinationAddressPersistentLocalId) + { + return WithReaddress(new AddressRegistryReaddress( + new ReaddressData( + new AddressPersistentLocalId(sourceAddressPersistentLocalId), + new AddressPersistentLocalId(destinationAddressPersistentLocalId)))); + } + + public ParcelAddressesWereReaddressedBuilder WithReaddress(AddressRegistryReaddress addressRegistryReaddress) + { + _addressRegistryReaddresses.Add(addressRegistryReaddress); + return this; + } + + public ParcelAddressesWereReaddressed Build() + { + var @event = new ParcelAddressesWereReaddressed( + fixture.Create(), + fixture.Create(), + _attachedAddressPersistentLocalIds, + _detachedAddressPersistentLocalIds, + _addressRegistryReaddresses); + + @event.SetFixtureProvenance(fixture); + + return @event; + } + } +} diff --git a/test/ParcelRegistry.Tests/Fixtures/WithUniqueInteger.cs b/test/ParcelRegistry.Tests/Fixtures/WithUniqueInteger.cs new file mode 100644 index 00000000..f77c9f7c --- /dev/null +++ b/test/ParcelRegistry.Tests/Fixtures/WithUniqueInteger.cs @@ -0,0 +1,23 @@ +namespace ParcelRegistry.Tests.Fixtures +{ + using System; + using AutoFixture.Kernel; + + public class WithUniqueInteger : ISpecimenBuilder + { + private int _lastInt; + + public object Create(object request, ISpecimenContext context) + { + if (request is not Type type || type != typeof(int)) + { + return new NoSpecimen(); + } + + var nextInt = _lastInt + 1; + _lastInt = nextInt; + + return nextInt; + } + } +} diff --git a/test/ParcelRegistry.Tests/InfrastructureEventsTests.cs b/test/ParcelRegistry.Tests/InfrastructureEventsTests.cs index 5b6b98ae..5999e144 100644 --- a/test/ParcelRegistry.Tests/InfrastructureEventsTests.cs +++ b/test/ParcelRegistry.Tests/InfrastructureEventsTests.cs @@ -5,10 +5,11 @@ namespace Be.Vlaanderen.Basisregisters.Testing.Infrastructure.Events using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; - using Be.Vlaanderen.Basisregisters.AggregateSource; - using Be.Vlaanderen.Basisregisters.EventHandling; + using AggregateSource; + using EventHandling; using FluentAssertions; using Newtonsoft.Json; + using ParcelRegistry.Parcel.Events; using Xunit; /// @@ -36,7 +37,9 @@ public InfrastructureEventsTests() _eventTypes = domainAssembly .GetTypes() - .Where(t => t.IsClass && t.Namespace != null && IsEventNamespace(t) && IsNotCompilerGenerated(t)); + .Where(t => + t.IsClass && t.Namespace != null && IsEventNamespace(t) && IsNotCompilerGenerated(t) + && t != typeof(AddressRegistryReaddress)); } [Fact] diff --git a/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs index 2f5c00a1..efe64fd0 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs @@ -4,7 +4,10 @@ using System.Threading.Tasks; using Api.BackOffice.Abstractions; using AutoFixture; + using Builders; + using Fixtures; using FluentAssertions; + using Parcel; using Parcel.Events; using Xunit; @@ -88,5 +91,51 @@ await Sut newAddressParcelRelation!.Count.Should().Be(2); }); } + + [Fact] + public async Task GivenParcelAddressesWereReaddressed_ThenAddressesAreAttachedAndDetached() + { + _fixture.Customize(new WithFixedParcelId()); + + var attachedAddressPersistentLocalIds = new[] { 1, 2, 3 }; + var detachedAddressPersistentLocalIds = new[] { 4, 5, 6 }; + + var eventBuilder = new ParcelAddressesWereReaddressedBuilder(_fixture); + + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + eventBuilder.WithAttachedAddress(addressPersistentLocalId); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + await AddRelation(_fixture.Create(), addressPersistentLocalId); + eventBuilder.WithDetachedAddress(addressPersistentLocalId); + } + + var @event = eventBuilder.Build(); + + await Sut + .Given(@event) + .Then(async _ => + { + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + var parcelAddressRelation = await _backOfficeContext.ParcelAddressRelations.FindAsync( + @event.ParcelId, + addressPersistentLocalId); + parcelAddressRelation.Should().NotBeNull(); + parcelAddressRelation!.Count.Should().Be(1); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + var parcelAddressRelation = await _backOfficeContext.ParcelAddressRelations.FindAsync( + @event.ParcelId, + addressPersistentLocalId); + parcelAddressRelation.Should().BeNull(); + } + }); + } } } diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests-Readdress.cs index 9f62a270..9225081d 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests-Readdress.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests-Readdress.cs @@ -2,6 +2,8 @@ { using System.Threading.Tasks; using AutoFixture; + using Builders; + using Fixtures; using FluentAssertions; using Parcel; using Parcel.Events; @@ -112,5 +114,67 @@ await Sut newRelation.Count.Should().Be(2); }); } + + [Fact] + public async Task GivenParcelAddressesWereReaddressed_ThenAddressesAreAttachedAndDetached() + { + _fixture.Customizations.Add(new WithUniqueInteger()); + + var firstParcelAddressWasAttachedV2 = _fixture.Create(); + var secondParcelAddressWasAttachedV2 = _fixture.Create(); + + var attachedAddressPersistentLocalIds = new[] + { + _fixture.Create(), + _fixture.Create() + }; + var detachedAddressPersistentLocalIds = new[] + { + firstParcelAddressWasAttachedV2.AddressPersistentLocalId, + secondParcelAddressWasAttachedV2.AddressPersistentLocalId + }; + + var eventBuilder = new ParcelAddressesWereReaddressedBuilder(_fixture); + + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + eventBuilder.WithAttachedAddress(addressPersistentLocalId); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + eventBuilder.WithDetachedAddress(addressPersistentLocalId); + } + + var @event = eventBuilder.Build(); + + await Sut + .Given( + _fixture.Create(), + firstParcelAddressWasAttachedV2, + secondParcelAddressWasAttachedV2, + @event) + .Then(async context => + { + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + var parcelAddressRelation = await context.ParcelLatestItemAddresses.FindAsync( + @event.ParcelId, + addressPersistentLocalId); + + parcelAddressRelation.Should().NotBeNull(); + parcelAddressRelation!.Count.Should().Be(1); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + var parcelAddressRelation = await context.ParcelLatestItemAddresses.FindAsync( + @event.ParcelId, + addressPersistentLocalId); + + parcelAddressRelation.Should().BeNull(); + } + }); + } } } diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests-Readdress.cs index efe2b886..54fff358 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests-Readdress.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests-Readdress.cs @@ -6,6 +6,7 @@ using Be.Vlaanderen.Basisregisters.GrAr.Common.Pipes; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; using Builders; + using Fixtures; using FluentAssertions; using Parcel.Events; using Xunit; @@ -205,5 +206,98 @@ await Sut parcelVersion!.Type.Should().Be("EventName"); }); } + + [Fact] + public async Task GivenParcelAddressesWereReaddressed_ThenAddressesAreAttachedAndDetached() + { + _fixture.Customizations.Add(new WithUniqueInteger()); + + var parcelWasImported = _fixture.Create(); + var firstParcelAddressWasAttachedV2 = _fixture.Create(); + var secondParcelAddressWasAttachedV2 = _fixture.Create(); + + var attachedAddressPersistentLocalIds = new[] + { + _fixture.Create(), + _fixture.Create() + }; + var detachedAddressPersistentLocalIds = new[] + { + firstParcelAddressWasAttachedV2.AddressPersistentLocalId, + secondParcelAddressWasAttachedV2.AddressPersistentLocalId + }; + + var eventBuilder = new ParcelAddressesWereReaddressedBuilder(_fixture); + + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + eventBuilder.WithAttachedAddress(addressPersistentLocalId); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + eventBuilder.WithDetachedAddress(addressPersistentLocalId); + } + + var @event = eventBuilder.Build(); + + var position = _fixture.Create(); + var parcelWasImportedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, _fixture.Create() }, + { Envelope.PositionMetadataKey, position }, + { Envelope.EventNameMetadataKey, _fixture.Create()} + }; + var firstParcelAddressWasAttachedV2Metadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, _fixture.Create() }, + { Envelope.PositionMetadataKey, ++position }, + { Envelope.EventNameMetadataKey, _fixture.Create()} + }; + var secondParcelAddressWasAttachedV2Metadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, _fixture.Create() }, + { Envelope.PositionMetadataKey, ++position }, + { Envelope.EventNameMetadataKey, _fixture.Create()} + }; + var eventMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, _fixture.Create() }, + { Envelope.PositionMetadataKey, ++position }, + { Envelope.EventNameMetadataKey, "EventName"} + }; + + await Sut + .Given( + new Envelope(new Envelope(parcelWasImported, parcelWasImportedMetadata)), + new Envelope( + new Envelope(firstParcelAddressWasAttachedV2, firstParcelAddressWasAttachedV2Metadata)), + new Envelope( + new Envelope(secondParcelAddressWasAttachedV2, secondParcelAddressWasAttachedV2Metadata)), + new Envelope(new Envelope(@event, eventMetadata))) + .Then(async context => + { + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + var parcelAddressRelation = await context.ParcelVersionAddresses.FindAsync( + position, + @event.ParcelId, + addressPersistentLocalId); + + parcelAddressRelation.Should().NotBeNull(); + parcelAddressRelation!.Count.Should().Be(1); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + var parcelAddressRelation = await context.ParcelVersionAddresses.FindAsync( + position, + @event.ParcelId, + addressPersistentLocalId); + + parcelAddressRelation.Should().BeNull(); + } + }); + } } } diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests-Readdress.cs index a4b6fbc5..b302c703 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests-Readdress.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests-Readdress.cs @@ -7,6 +7,7 @@ namespace ParcelRegistry.Tests.ProjectionTests.Legacy using Be.Vlaanderen.Basisregisters.GrAr.Common.Pipes; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; using Builders; + using Fixtures; using FluentAssertions; using Parcel.Events; using Xunit; @@ -172,5 +173,88 @@ await Sut parcelDetailV2.LastEventHash.Should().Be(@event.GetHash()); }); } + + [Fact] + public async Task GivenParcelAddressesWereReaddressed_ThenAddressesAreAttachedAndDetached() + { + _fixture.Customizations.Add(new WithUniqueInteger()); + + var parcelWasImported = _fixture.Create(); + var firstParcelAddressWasAttachedV2 = _fixture.Create(); + var secondParcelAddressWasAttachedV2 = _fixture.Create(); + + var attachedAddressPersistentLocalIds = new[] + { + _fixture.Create(), + _fixture.Create() + }; + var detachedAddressPersistentLocalIds = new[] + { + firstParcelAddressWasAttachedV2.AddressPersistentLocalId, + secondParcelAddressWasAttachedV2.AddressPersistentLocalId + }; + + var eventBuilder = new ParcelAddressesWereReaddressedBuilder(_fixture); + + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + eventBuilder.WithAttachedAddress(addressPersistentLocalId); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + eventBuilder.WithDetachedAddress(addressPersistentLocalId); + } + + var @event = eventBuilder.Build(); + + var parcelWasImportedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, parcelWasImported.GetHash() } + }; + var firstParcelAddressWasAttachedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, firstParcelAddressWasAttachedV2.GetHash() } + }; + var secondParcelAddressWasAttachedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, secondParcelAddressWasAttachedV2.GetHash() } + }; + var eventMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, @event.GetHash() } + }; + + await Sut + .Given( + new Envelope(new Envelope(parcelWasImported, parcelWasImportedMetadata)), + new Envelope( + new Envelope(firstParcelAddressWasAttachedV2, firstParcelAddressWasAttachedMetadata)), + new Envelope( + new Envelope(secondParcelAddressWasAttachedV2, secondParcelAddressWasAttachedMetadata)), + new Envelope(new Envelope(@event, eventMetadata))) + .Then(async context => + { + var parcelDetailV2 = await context.ParcelDetailWithCountV2.FindAsync(parcelWasImported.ParcelId); + parcelDetailV2.Should().NotBeNull(); + + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + var parcelAddressRelation = parcelDetailV2!.Addresses + .SingleOrDefault(x => x.AddressPersistentLocalId == addressPersistentLocalId); + + parcelAddressRelation.Should().NotBeNull(); + parcelAddressRelation!.Count.Should().Be(1); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + var parcelAddressRelation = parcelDetailV2!.Addresses + .SingleOrDefault(x => x.AddressPersistentLocalId == addressPersistentLocalId); + + parcelAddressRelation.Should().BeNull(); + } + }); + } } } diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs index c8994b9a..3abf48ea 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs @@ -11,6 +11,7 @@ namespace ParcelRegistry.Tests.ProjectionTests.Legacy using Be.Vlaanderen.Basisregisters.GrAr.Provenance; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; using Be.Vlaanderen.Basisregisters.Utilities.HexByteConvertor; + using Builders; using EventExtensions; using Fixtures; using FluentAssertions; @@ -23,7 +24,7 @@ namespace ParcelRegistry.Tests.ProjectionTests.Legacy public class ParcelSyndicationV2Tests : ParcelLegacyProjectionTest { - private readonly Fixture? _fixture; + private readonly Fixture _fixture; public ParcelSyndicationV2Tests() { @@ -297,6 +298,91 @@ await Sut }); } + [Fact] + public async Task GivenParcelAddressesWereReaddressed_ThenAddressesAreAttachedAndDetached() + { + _fixture.Customizations.Add(new WithUniqueInteger()); + + var parcelWasImported = _fixture.Create(); + var firstParcelAddressWasAttachedV2 = _fixture.Create(); + var secondParcelAddressWasAttachedV2 = _fixture.Create(); + + var attachedAddressPersistentLocalIds = new[] + { + _fixture.Create(), + _fixture.Create() + }; + var detachedAddressPersistentLocalIds = new[] + { + firstParcelAddressWasAttachedV2.AddressPersistentLocalId, + secondParcelAddressWasAttachedV2.AddressPersistentLocalId + }; + + var eventBuilder = new ParcelAddressesWereReaddressedBuilder(_fixture); + + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + eventBuilder.WithAttachedAddress(addressPersistentLocalId); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + eventBuilder.WithDetachedAddress(addressPersistentLocalId); + } + + var @event = eventBuilder.Build(); + + var position = _fixture.Create(); + var parcelWasImportedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, parcelWasImported.GetHash() }, + { Envelope.PositionMetadataKey, position }, + { Envelope.EventNameMetadataKey, nameof(ParcelWasImported) }, + }; + var firstParcelAddressWasAttachedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, parcelWasImported.GetHash() }, + { Envelope.PositionMetadataKey, ++position }, + { Envelope.EventNameMetadataKey, nameof(ParcelAddressWasAttachedV2) }, + }; + var secondParcelAddressWasAttachedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, parcelWasImported.GetHash() }, + { Envelope.PositionMetadataKey, ++position }, + { Envelope.EventNameMetadataKey, nameof(ParcelAddressWasAttachedV2) }, + }; + var eventMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, parcelWasImported.GetHash() }, + { Envelope.PositionMetadataKey, ++position }, + { Envelope.EventNameMetadataKey, nameof(ParcelAddressesWereReaddressed) }, + }; + + await Sut + .Given( + new Envelope(new Envelope(parcelWasImported, parcelWasImportedMetadata)), + new Envelope( + new Envelope(firstParcelAddressWasAttachedV2, firstParcelAddressWasAttachedMetadata)), + new Envelope( + new Envelope(secondParcelAddressWasAttachedV2, secondParcelAddressWasAttachedMetadata)), + new Envelope(new Envelope(@event, eventMetadata))) + .Then(async context => + { + var parcelSyndicationItem = await context.ParcelSyndication.FindAsync(position); + parcelSyndicationItem.Should().NotBeNull(); + + foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) + { + parcelSyndicationItem!.AddressPersistentLocalIds.Should().Contain(addressPersistentLocalId); + } + + foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) + { + parcelSyndicationItem!.AddressPersistentLocalIds.Should().NotContain(addressPersistentLocalId); + } + }); + } + protected override ParcelSyndicationProjections CreateProjection() => new ParcelSyndicationProjections(); } From 87d4bb06e010425398ad58714dcb045a2c2ef3cc Mon Sep 17 00:00:00 2001 From: jvandaal Date: Thu, 16 May 2024 16:40:27 +0200 Subject: [PATCH 09/13] feat: update backoffice context after consume (not done) --- .../BackOfficeConsumer.cs | 6 ++- .../Projections/CommandHandler.cs | 19 ++++++++ .../CommandHandlingKafkaProjection.cs | 37 +++++++++++++-- .../CommandHandlingKafkaProjectionTests.cs | 47 ++++++++++++++++++- 4 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/ParcelRegistry.Consumer.Address/BackOfficeConsumer.cs b/src/ParcelRegistry.Consumer.Address/BackOfficeConsumer.cs index dd484088..79641938 100644 --- a/src/ParcelRegistry.Consumer.Address/BackOfficeConsumer.cs +++ b/src/ParcelRegistry.Consumer.Address/BackOfficeConsumer.cs @@ -10,6 +10,7 @@ namespace ParcelRegistry.Consumer.Address using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; + using Parcel; using Projections; public sealed class BackOfficeConsumer : BackgroundService @@ -17,6 +18,7 @@ public sealed class BackOfficeConsumer : BackgroundService private readonly ILifetimeScope _lifetimeScope; private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly IDbContextFactory _backOfficeContextFactory; + private readonly IParcels _parcels; private readonly ILoggerFactory _loggerFactory; private readonly IIdempotentConsumer _kafkaIdemIdompotencyConsumer; private readonly ILogger _logger; @@ -25,12 +27,14 @@ public BackOfficeConsumer( ILifetimeScope lifetimeScope, IHostApplicationLifetime hostApplicationLifetime, IDbContextFactory backOfficeContextFactory, + IParcels parcels, ILoggerFactory loggerFactory, IIdempotentConsumer kafkaIdemIdompotencyConsumer) { _lifetimeScope = lifetimeScope; _hostApplicationLifetime = hostApplicationLifetime; _backOfficeContextFactory = backOfficeContextFactory; + _parcels = parcels; _loggerFactory = loggerFactory; _kafkaIdemIdompotencyConsumer = kafkaIdemIdompotencyConsumer; @@ -45,7 +49,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) var commandHandlingProjector = new ConnectedProjector( Resolve.WhenEqualToHandlerMessageType( - new CommandHandlingKafkaProjection(_backOfficeContextFactory).Handlers)); + new CommandHandlingKafkaProjection(_backOfficeContextFactory, _parcels).Handlers)); var commandHandler = new CommandHandler(_lifetimeScope, _loggerFactory); diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandler.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandler.cs index 2f3a07cb..ba54f438 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandler.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandler.cs @@ -1,9 +1,11 @@ namespace ParcelRegistry.Consumer.Address.Projections { + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Autofac; using Be.Vlaanderen.Basisregisters.CommandHandling; + using Be.Vlaanderen.Basisregisters.CommandHandling.Idempotency; using Be.Vlaanderen.Basisregisters.GrAr.Provenance; using Microsoft.Extensions.Logging; @@ -30,5 +32,22 @@ public virtual async Task Handle(T command, CancellationToken cancellationTok _logger.LogDebug($"Handled {command.GetType().FullName}"); } + + public virtual async Task HandleIdempotent(T command, CancellationToken cancellationToken) + where T : class, IHasCommandProvenance + { + _logger.LogDebug($"Idempotently handling {command.GetType().FullName}"); + + await using var scope = _container.BeginLifetimeScope(); + + var resolver = scope.Resolve(); + _ = await resolver.Dispatch( + command.CreateCommandId(), + command, + new Dictionary(), + cancellationToken: cancellationToken); + + _logger.LogDebug($"Idempotently handled {command.GetType().FullName}"); + } } } diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index fd56ff8e..80ccd1ca 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -19,7 +19,9 @@ public sealed class CommandHandlingKafkaProjection : ConnectedProjection _backOfficeContextFactory; - public CommandHandlingKafkaProjection(IDbContextFactory backOfficeContextFactory) + public CommandHandlingKafkaProjection( + IDbContextFactory backOfficeContextFactory, + IParcels parcels) { _backOfficeContextFactory = backOfficeContextFactory; @@ -179,24 +181,51 @@ await DetachBecauseRemoved( .Select(x => (int)x.SourceAddressPersistentLocalId) .ToList(); - var parcelAddressRelations = await backOfficeContext.ParcelAddressRelations + var sourceAddressParcelRelations = await backOfficeContext.ParcelAddressRelations .AsNoTracking() .Where(x => sourceAddressPersistentLocalIds.Contains(x.AddressPersistentLocalId)) .ToListAsync(cancellationToken: ct); - var commandByParcels = parcelAddressRelations + var commandByParcels = sourceAddressParcelRelations .GroupBy( relation => relation.ParcelId, relation => readdresses.Where(x => x.SourceAddressPersistentLocalId == relation.AddressPersistentLocalId)) .Select(x => new ReaddressAddresses( new ParcelId(x.Key), x.SelectMany(a => a), - FromProvenance(message.Provenance))); + FromProvenance(message.Provenance))) + .ToList(); foreach (var command in commandByParcels) { await commandHandler.Handle(command, ct); } + + foreach (var parcelId in commandByParcels.Select(x => x.ParcelId)) + { + var parcel = await parcels.GetAsync(new ParcelStreamId(parcelId), ct); + + var backOfficeAddresses = (await backOfficeContext.ParcelAddressRelations + .AsNoTracking() + .Where(x => x.ParcelId == parcelId) + .Select(x => x.AddressPersistentLocalId) + .ToListAsync(cancellationToken: ct)) + .Select(x => new AddressPersistentLocalId(x)) + .ToList(); + + var addressesToRemove = backOfficeAddresses.Except(parcel.AddressPersistentLocalIds).ToList(); + var addressesToAdd = parcel.AddressPersistentLocalIds.Except(backOfficeAddresses).ToList(); + + foreach (var addressPersistentLocalId in addressesToRemove) + { + await backOfficeContext.RemoveIdempotentParcelAddressRelation(parcelId, addressPersistentLocalId, ct); + } + + foreach (var addressPersistentLocalId in addressesToAdd) + { + await backOfficeContext.AddIdempotentParcelAddressRelation(parcelId, addressPersistentLocalId, ct); + } + } }); When(async (commandHandler, message, ct) => diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs index df9960b7..057ea1b6 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs @@ -1,13 +1,17 @@ namespace ParcelRegistry.Tests.ProjectionTests.Consumer.Address { using System; + using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Api.BackOffice.Abstractions; + using Autofac; using AutoFixture; + using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting; using Be.Vlaanderen.Basisregisters.GrAr.Contracts.AddressRegistry; using Be.Vlaanderen.Basisregisters.GrAr.Provenance; + using EventExtensions; using Fixtures; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging.Abstractions; @@ -15,6 +19,7 @@ namespace ParcelRegistry.Tests.ProjectionTests.Consumer.Address using NodaTime; using Parcel; using Parcel.Commands; + using Parcel.Events; using ParcelRegistry.Consumer.Address; using ParcelRegistry.Consumer.Address.Projections; using Tests.BackOffice; @@ -26,6 +31,7 @@ public sealed class CommandHandlingKafkaProjectionTests : KafkaProjectionTest _mockCommandHandler; + private readonly Mock _parcels; public CommandHandlingKafkaProjectionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { @@ -33,6 +39,7 @@ public CommandHandlingKafkaProjectionTests(ITestOutputHelper testOutputHelper) : _mockCommandHandler = new Mock(); _fakeBackOfficeContext = new FakeBackOfficeContextFactory().CreateDbContext([]); + _parcels = new Mock(); } [Fact] @@ -674,6 +681,38 @@ public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() AddParcelAddressRelations(parcelTwoId, parcelTwoAddressPersistentLocalIds); AddParcelAddressRelations(Fixture.Create(), [6, 7, 8]); + var events = new List(); + foreach (var addressPersistentLocalId in parcelOneAddressPersistentLocalIds) + { + var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( + parcelOneId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); + parcelAddressWasAttached.SetFixtureProvenance(Fixture); + events.Add(parcelAddressWasAttached); + } + + var parcelOne = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + parcelOne.Initialize(events); + + _parcels + .Setup(x => x.GetAsync(new ParcelStreamId(parcelOneId), It.IsAny())) + .ReturnsAsync(parcelOne); + + events.Clear(); + foreach (var addressPersistentLocalId in parcelTwoAddressPersistentLocalIds) + { + var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( + parcelTwoId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); + parcelAddressWasAttached.SetFixtureProvenance(Fixture); + events.Add(parcelAddressWasAttached); + } + + var parcelTwo = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + parcelTwo.Initialize(events); + + _parcels + .Setup(x => x.GetAsync(new ParcelStreamId(parcelTwoId), It.IsAny())) + .ReturnsAsync(parcelTwo); + var @event = new StreetNameWasReaddressed( Fixture.Create(), new[] @@ -725,6 +764,12 @@ await Then(async _ => && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdThree)), CancellationToken.None), Times.Once); + + foreach (var addressPersistentLocalId in new[] { sourceAddressPersistentLocalIdOne, sourceAddressPersistentLocalIdTwo, sourceAddressPersistentLocalIdThree}) + { + // Todo + } + await Task.CompletedTask; }); } @@ -769,7 +814,7 @@ protected override CommandHandlingKafkaProjection CreateProjection() factoryMock .Setup(x => x.CreateDbContextAsync(CancellationToken.None)) .Returns(Task.FromResult(_fakeBackOfficeContext)); - return new CommandHandlingKafkaProjection(factoryMock.Object); + return new CommandHandlingKafkaProjection(factoryMock.Object, _parcels.Object); } } From 7dc68122769e1e939a35f606201e1043ae002efd Mon Sep 17 00:00:00 2001 From: jvandaal Date: Fri, 17 May 2024 10:53:32 +0200 Subject: [PATCH 10/13] test: add consumer test --- ...dHandlingKafkaProjectionTests-Readdress.cs | 182 +++++++++++++++ .../CommandHandlingKafkaProjectionTests.cs | 211 +----------------- 2 files changed, 185 insertions(+), 208 deletions(-) create mode 100644 test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs new file mode 100644 index 00000000..24bbf817 --- /dev/null +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs @@ -0,0 +1,182 @@ +namespace ParcelRegistry.Tests.ProjectionTests.Consumer.Address +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Autofac; + using AutoFixture; + using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting; + using Be.Vlaanderen.Basisregisters.GrAr.Contracts.AddressRegistry; + using Be.Vlaanderen.Basisregisters.GrAr.Provenance; + using EventExtensions; + using FluentAssertions; + using Moq; + using NodaTime; + using Parcel; + using Parcel.Commands; + using Parcel.Events; + using Xunit; + using Provenance = Be.Vlaanderen.Basisregisters.GrAr.Contracts.Common.Provenance; + + public partial class CommandHandlingKafkaProjectionTests + { + + + [Fact] + public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() + { + var parcelOneId = Fixture.Create(); + var parcelTwoId = Fixture.Create(); + + var sourceAddressPersistentLocalIdOne = 1; + var sourceAddressPersistentLocalIdTwo = 2; + var sourceAddressPersistentLocalIdThree = 5; + var unattachedSourceAddressPersistentLocalIdOne = 20; + var destinationAddressPersistentLocalIdOne = 10; + var destinationAddressPersistentLocalIdTwo = 11; + var destinationAddressPersistentLocalIdThree = 12; + + var parcelOneAddressPersistentLocalIds = new[] { sourceAddressPersistentLocalIdOne, sourceAddressPersistentLocalIdTwo, 3 }; + var parcelTwoAddressPersistentLocalIds = new[] { 4, sourceAddressPersistentLocalIdThree }; + + var parcelOneExpectedAddressPersistentLocalIds = + new[] { destinationAddressPersistentLocalIdOne, destinationAddressPersistentLocalIdTwo, 3 }; + var parcelTwoExpectedAddressPersistentLocalIds = new[] { 4, destinationAddressPersistentLocalIdThree }; + + AddParcelAddressRelations(parcelOneId, parcelOneAddressPersistentLocalIds); + AddParcelAddressRelations(parcelTwoId, parcelTwoAddressPersistentLocalIds); + AddParcelAddressRelations(Fixture.Create(), [6, 7, 8]); + + var events = new List(); + foreach (var addressPersistentLocalId in parcelOneExpectedAddressPersistentLocalIds) + { + var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( + parcelOneId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); + parcelAddressWasAttached.SetFixtureProvenance(Fixture); + events.Add(parcelAddressWasAttached); + } + + var parcelOne = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + parcelOne.Initialize(events); + + _parcels + .Setup(x => x.GetAsync(new ParcelStreamId(parcelOneId), It.IsAny())) + .ReturnsAsync(parcelOne); + + events.Clear(); + foreach (var addressPersistentLocalId in parcelTwoExpectedAddressPersistentLocalIds) + { + var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( + parcelTwoId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); + parcelAddressWasAttached.SetFixtureProvenance(Fixture); + events.Add(parcelAddressWasAttached); + } + + var parcelTwo = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + parcelTwo.Initialize(events); + + _parcels + .Setup(x => x.GetAsync(new ParcelStreamId(parcelTwoId), It.IsAny())) + .ReturnsAsync(parcelTwo); + + var @event = new StreetNameWasReaddressed( + Fixture.Create(), + new[] + { + new AddressHouseNumberReaddressedData( + destinationAddressPersistentLocalIdOne, + CreateReaddressedAddressData(sourceAddressPersistentLocalIdOne, destinationAddressPersistentLocalIdOne), + new[] + { + CreateReaddressedAddressData(sourceAddressPersistentLocalIdTwo, destinationAddressPersistentLocalIdTwo), + CreateReaddressedAddressData(unattachedSourceAddressPersistentLocalIdOne, 21) + }), + new AddressHouseNumberReaddressedData( + destinationAddressPersistentLocalIdThree, + CreateReaddressedAddressData(sourceAddressPersistentLocalIdThree, destinationAddressPersistentLocalIdThree), + Array.Empty()), + }, + new Provenance( + Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), + Application.ParcelRegistry.ToString(), + Modification.Update.ToString(), + Organisation.Aiv.ToString(), + "test") + ); + + Given(@event); + await Then(async _ => + { + _mockCommandHandler.Verify(x => + x.Handle( + It.Is(y => + y.ParcelId == parcelOneId + && y.Readdresses.Count == 2 + && y.Readdresses.Any(z => + z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdOne + && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdOne) + && y.Readdresses.Any(z => + z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdTwo + && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdTwo)), + CancellationToken.None), + Times.Once); + _mockCommandHandler.Verify(x => + x.Handle( + It.Is(y => + y.ParcelId == parcelTwoId + && y.Readdresses.Count == 1 + && y.Readdresses.Any(z => + z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdThree + && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdThree)), + CancellationToken.None), + Times.Once); + + var parcelOneRelations = _fakeBackOfficeContext.ParcelAddressRelations + .Where(x => x.ParcelId == parcelOneId) + .ToList(); + parcelOneRelations.Count.Should().Be(parcelOneExpectedAddressPersistentLocalIds.Length); + foreach (var addressPersistentLocalId in parcelOneExpectedAddressPersistentLocalIds) + { + var expectedRelation = + parcelOneRelations.SingleOrDefault(x => x.AddressPersistentLocalId == addressPersistentLocalId); + expectedRelation.Should().NotBeNull(); + expectedRelation!.Count.Should().Be(1); + } + + var parcelTwoRelations = _fakeBackOfficeContext.ParcelAddressRelations + .Where(x => x.ParcelId == parcelTwoId) + .ToList(); + parcelTwoRelations.Count.Should().Be(parcelTwoExpectedAddressPersistentLocalIds.Length); + foreach (var addressPersistentLocalId in parcelTwoExpectedAddressPersistentLocalIds) + { + var expectedRelation = + parcelTwoRelations.SingleOrDefault(x => x.AddressPersistentLocalId == addressPersistentLocalId); + expectedRelation.Should().NotBeNull(); + expectedRelation!.Count.Should().Be(1); + } + + await Task.CompletedTask; + }); + } + + private ReaddressedAddressData CreateReaddressedAddressData( + int sourceAddressPersistentLocalIdOne, + int destinationAddressPersistentLocalIdOne) + { + return new ReaddressedAddressData( + sourceAddressPersistentLocalIdOne, + destinationAddressPersistentLocalIdOne, + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create(), + Fixture.Create()); + } + } +} diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs index 057ea1b6..8c1e2120 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs @@ -13,6 +13,7 @@ namespace ParcelRegistry.Tests.ProjectionTests.Consumer.Address using Be.Vlaanderen.Basisregisters.GrAr.Provenance; using EventExtensions; using Fixtures; + using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging.Abstractions; using Moq; @@ -27,7 +28,7 @@ namespace ParcelRegistry.Tests.ProjectionTests.Consumer.Address using Xunit.Abstractions; using Provenance = Be.Vlaanderen.Basisregisters.GrAr.Contracts.Common.Provenance; - public sealed class CommandHandlingKafkaProjectionTests : KafkaProjectionTest + public partial class CommandHandlingKafkaProjectionTests : KafkaProjectionTest { private readonly FakeBackOfficeContext _fakeBackOfficeContext; private readonly Mock _mockCommandHandler; @@ -38,7 +39,7 @@ public CommandHandlingKafkaProjectionTests(ITestOutputHelper testOutputHelper) : Fixture.Customize(new InfrastructureCustomization()); _mockCommandHandler = new Mock(); - _fakeBackOfficeContext = new FakeBackOfficeContextFactory().CreateDbContext([]); + _fakeBackOfficeContext = new FakeBackOfficeContextFactory(dispose: false).CreateDbContext([]); _parcels = new Mock(); } @@ -532,80 +533,6 @@ await Then(async _ => }); } - // [Fact] - // public async Task StreetNameWasReaddressed() - // { - // var sourceAddressPersistentLocalId = 1; - // var sourceBoxNumberAddressPersistentLocalId = 2; - // var destinationAddressPersistentLocalId = 3; - // var destinationBoxNumberAddressPersistentLocalId = 4; - // - // var @event = new AddressHouseNumberWasReaddressed( - // 1000000, - // sourceAddressPersistentLocalId, - // new ReaddressedAddressData( - // sourceAddressPersistentLocalId, - // destinationAddressPersistentLocalId, - // true, - // "Current", - // "120", - // null, - // "9000", - // "AppointedByAdministrator", - // "Entry", - // "ExtendedWkbGeometry", - // true), - // new[] - // { - // new ReaddressedAddressData( - // sourceBoxNumberAddressPersistentLocalId, - // destinationBoxNumberAddressPersistentLocalId, - // true, - // "Current", - // "120", - // "A", - // "9000", - // "AppointedByAdministrator", - // "Entry", - // "ExtendedWkbGeometry", - // true), - // }, - // new Provenance( - // Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), - // Application.ParcelRegistry.ToString(), - // Modification.Update.ToString(), - // Organisation.Aiv.ToString(), - // "test")); - // - // AddParcelAddressRelations(sourceAddressPersistentLocalId); - // AddParcelAddressRelations(sourceBoxNumberAddressPersistentLocalId); - // - // Given(@event); - // await Then(async _ => - // { - // _mockCommandHandler.Verify(x => - // x.Handle(It.IsAny(), CancellationToken.None), - // Times.Exactly(2)); - // - // _mockCommandHandler.Verify(x => - // x.Handle( - // It.Is(y => - // y.NewAddressPersistentLocalId == destinationAddressPersistentLocalId - // && y.PreviousAddressPersistentLocalId == sourceAddressPersistentLocalId), - // CancellationToken.None), - // Times.Exactly(1)); - // _mockCommandHandler.Verify(x => - // x.Handle( - // It.Is(y => - // y.NewAddressPersistentLocalId == destinationBoxNumberAddressPersistentLocalId - // && y.PreviousAddressPersistentLocalId == sourceBoxNumberAddressPersistentLocalId), - // CancellationToken.None), - // Times.Exactly(1)); - // - // await Task.CompletedTask; - // }); - // } - [Fact] public async Task DetachAddressBecauseAddressWasRejectedBecauseOfReaddress() { @@ -660,138 +587,6 @@ await Then(async _ => }); } - [Fact] - public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() - { - var parcelOneId = Fixture.Create(); - var parcelTwoId = Fixture.Create(); - - var sourceAddressPersistentLocalIdOne = 1; - var sourceAddressPersistentLocalIdTwo = 2; - var sourceAddressPersistentLocalIdThree = 5; - var unattachedSourceAddressPersistentLocalIdOne = 20; - var destinationAddressPersistentLocalIdOne = 10; - var destinationAddressPersistentLocalIdTwo = 11; - var destinationAddressPersistentLocalIdThree = 12; - - var parcelOneAddressPersistentLocalIds = new[] { sourceAddressPersistentLocalIdOne, sourceAddressPersistentLocalIdTwo, 3 }; - var parcelTwoAddressPersistentLocalIds = new[] { 4, sourceAddressPersistentLocalIdThree }; - - AddParcelAddressRelations(parcelOneId, parcelOneAddressPersistentLocalIds); - AddParcelAddressRelations(parcelTwoId, parcelTwoAddressPersistentLocalIds); - AddParcelAddressRelations(Fixture.Create(), [6, 7, 8]); - - var events = new List(); - foreach (var addressPersistentLocalId in parcelOneAddressPersistentLocalIds) - { - var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( - parcelOneId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); - parcelAddressWasAttached.SetFixtureProvenance(Fixture); - events.Add(parcelAddressWasAttached); - } - - var parcelOne = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); - parcelOne.Initialize(events); - - _parcels - .Setup(x => x.GetAsync(new ParcelStreamId(parcelOneId), It.IsAny())) - .ReturnsAsync(parcelOne); - - events.Clear(); - foreach (var addressPersistentLocalId in parcelTwoAddressPersistentLocalIds) - { - var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( - parcelTwoId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); - parcelAddressWasAttached.SetFixtureProvenance(Fixture); - events.Add(parcelAddressWasAttached); - } - - var parcelTwo = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); - parcelTwo.Initialize(events); - - _parcels - .Setup(x => x.GetAsync(new ParcelStreamId(parcelTwoId), It.IsAny())) - .ReturnsAsync(parcelTwo); - - var @event = new StreetNameWasReaddressed( - Fixture.Create(), - new[] - { - new AddressHouseNumberReaddressedData( - destinationAddressPersistentLocalIdOne, - CreateReaddressedAddressData(sourceAddressPersistentLocalIdOne, destinationAddressPersistentLocalIdOne), - new[] - { - CreateReaddressedAddressData(sourceAddressPersistentLocalIdTwo, destinationAddressPersistentLocalIdTwo), - CreateReaddressedAddressData(unattachedSourceAddressPersistentLocalIdOne, 21), - }), - new AddressHouseNumberReaddressedData( - destinationAddressPersistentLocalIdThree, - CreateReaddressedAddressData(sourceAddressPersistentLocalIdThree, destinationAddressPersistentLocalIdThree), - Array.Empty()), - }, - new Provenance( - Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), - Application.ParcelRegistry.ToString(), - Modification.Update.ToString(), - Organisation.Aiv.ToString(), - "test") - ); - - Given(@event); - await Then(async _ => - { - _mockCommandHandler.Verify(x => - x.Handle( - It.Is(y => - y.ParcelId == parcelOneId - && y.Readdresses.Count == 2 - && y.Readdresses.Any(z => - z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdOne - && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdOne) - && y.Readdresses.Any(z => - z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdTwo - && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdTwo)), - CancellationToken.None), - Times.Once); - _mockCommandHandler.Verify(x => - x.Handle( - It.Is(y => - y.ParcelId == parcelTwoId - && y.Readdresses.Count == 1 - && y.Readdresses.Any(z => - z.SourceAddressPersistentLocalId == sourceAddressPersistentLocalIdThree - && z.DestinationAddressPersistentLocalId == destinationAddressPersistentLocalIdThree)), - CancellationToken.None), - Times.Once); - - foreach (var addressPersistentLocalId in new[] { sourceAddressPersistentLocalIdOne, sourceAddressPersistentLocalIdTwo, sourceAddressPersistentLocalIdThree}) - { - // Todo - } - - await Task.CompletedTask; - }); - } - - private ReaddressedAddressData CreateReaddressedAddressData( - int sourceAddressPersistentLocalIdOne, - int destinationAddressPersistentLocalIdOne) - { - return new ReaddressedAddressData( - sourceAddressPersistentLocalIdOne, - destinationAddressPersistentLocalIdOne, - Fixture.Create(), - Fixture.Create(), - Fixture.Create(), - Fixture.Create(), - Fixture.Create(), - Fixture.Create(), - Fixture.Create(), - Fixture.Create(), - Fixture.Create()); - } - private void AddParcelAddressRelations(ParcelId parcelId, int[] addressPersistentLocalIds) { foreach (var addressPersistentLocalId in addressPersistentLocalIds) From 26f4807fdebb51c181374783ab6ecfa244260a8d Mon Sep 17 00:00:00 2001 From: Rik De Peuter Date: Fri, 17 May 2024 11:16:36 +0200 Subject: [PATCH 11/13] chore: refactor test --- ...dHandlingKafkaProjectionTests-Readdress.cs | 63 +++++++++---------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs index 24bbf817..5c942168 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs @@ -22,8 +22,6 @@ namespace ParcelRegistry.Tests.ProjectionTests.Consumer.Address public partial class CommandHandlingKafkaProjectionTests { - - [Fact] public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() { @@ -45,42 +43,16 @@ public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() new[] { destinationAddressPersistentLocalIdOne, destinationAddressPersistentLocalIdTwo, 3 }; var parcelTwoExpectedAddressPersistentLocalIds = new[] { 4, destinationAddressPersistentLocalIdThree }; + // Setup BackofficeContext AddParcelAddressRelations(parcelOneId, parcelOneAddressPersistentLocalIds); AddParcelAddressRelations(parcelTwoId, parcelTwoAddressPersistentLocalIds); AddParcelAddressRelations(Fixture.Create(), [6, 7, 8]); - var events = new List(); - foreach (var addressPersistentLocalId in parcelOneExpectedAddressPersistentLocalIds) - { - var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( - parcelOneId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); - parcelAddressWasAttached.SetFixtureProvenance(Fixture); - events.Add(parcelAddressWasAttached); - } - - var parcelOne = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); - parcelOne.Initialize(events); - - _parcels - .Setup(x => x.GetAsync(new ParcelStreamId(parcelOneId), It.IsAny())) - .ReturnsAsync(parcelOne); - - events.Clear(); - foreach (var addressPersistentLocalId in parcelTwoExpectedAddressPersistentLocalIds) - { - var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( - parcelTwoId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); - parcelAddressWasAttached.SetFixtureProvenance(Fixture); - events.Add(parcelAddressWasAttached); - } - - var parcelTwo = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); - parcelTwo.Initialize(events); - - _parcels - .Setup(x => x.GetAsync(new ParcelStreamId(parcelTwoId), It.IsAny())) - .ReturnsAsync(parcelTwo); + // Setup domain + SetupParcelWithAddresses(parcelOneId, parcelOneExpectedAddressPersistentLocalIds); + SetupParcelWithAddresses(parcelTwoId, parcelTwoExpectedAddressPersistentLocalIds); + // Act var @event = new StreetNameWasReaddressed( Fixture.Create(), new[] @@ -96,7 +68,7 @@ public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() new AddressHouseNumberReaddressedData( destinationAddressPersistentLocalIdThree, CreateReaddressedAddressData(sourceAddressPersistentLocalIdThree, destinationAddressPersistentLocalIdThree), - Array.Empty()), + []) }, new Provenance( Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), @@ -107,6 +79,8 @@ public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() ); Given(@event); + + // Assert await Then(async _ => { _mockCommandHandler.Verify(x => @@ -133,6 +107,8 @@ await Then(async _ => CancellationToken.None), Times.Once); + _mockCommandHandler.Invocations.Count.Should().Be(2); + var parcelOneRelations = _fakeBackOfficeContext.ParcelAddressRelations .Where(x => x.ParcelId == parcelOneId) .ToList(); @@ -161,6 +137,25 @@ await Then(async _ => }); } + private void SetupParcelWithAddresses(ParcelId parcelId, IEnumerable addressPersistentLocalIds) + { + var parcel = new ParcelFactory(NoSnapshotStrategy.Instance, Container.Resolve()).Create(); + var events = addressPersistentLocalIds + .Select(addressPersistentLocalId => + { + var parcelAddressWasAttached = new ParcelAddressWasAttachedV2( + parcelId, Fixture.Create(), new AddressPersistentLocalId(addressPersistentLocalId)); + parcelAddressWasAttached.SetFixtureProvenance(Fixture); + return parcelAddressWasAttached; + }) + .ToList(); + parcel.Initialize(events); + + _parcels + .Setup(x => x.GetAsync(new ParcelStreamId(parcelId), It.IsAny())) + .ReturnsAsync(parcel); + } + private ReaddressedAddressData CreateReaddressedAddressData( int sourceAddressPersistentLocalIdOne, int destinationAddressPersistentLocalIdOne) From 36377ca2d67544d30fe7375c4e246209bc8de00b Mon Sep 17 00:00:00 2001 From: Rik De Peuter Date: Fri, 17 May 2024 11:57:53 +0200 Subject: [PATCH 12/13] make commands idempontent --- .../Program.cs | 15 +++++- .../appsettings.json | 4 ++ .../CommandHandlingKafkaProjection.cs | 14 +++++- .../BackOfficeProjections.cs | 40 ++-------------- ...celBackOfficeProjectionsTests-Readdress.cs | 48 +------------------ ...dHandlingKafkaProjectionTests-Readdress.cs | 4 +- 6 files changed, 39 insertions(+), 86 deletions(-) diff --git a/src/ParcelRegistry.Consumer.Address.Console/Program.cs b/src/ParcelRegistry.Consumer.Address.Console/Program.cs index 864cb345..85f79982 100644 --- a/src/ParcelRegistry.Consumer.Address.Console/Program.cs +++ b/src/ParcelRegistry.Consumer.Address.Console/Program.cs @@ -9,11 +9,11 @@ namespace ParcelRegistry.Consumer.Address.Console using Autofac; using Autofac.Extensions.DependencyInjection; using Be.Vlaanderen.Basisregisters.Aws.DistributedMutex; + using Be.Vlaanderen.Basisregisters.CommandHandling.Idempotency; using Be.Vlaanderen.Basisregisters.EventHandling; using Be.Vlaanderen.Basisregisters.MessageHandling.Kafka; using Be.Vlaanderen.Basisregisters.MessageHandling.Kafka.Consumer; using Destructurama; - using Infrastructure; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -25,6 +25,7 @@ namespace ParcelRegistry.Consumer.Address.Console using Serilog; using Serilog.Debugging; using Serilog.Extensions.Logging; + using MigrationsHelper = Infrastructure.MigrationsHelper; public sealed class Program { @@ -145,6 +146,18 @@ public static async Task Main(string[] args) .As>() .SingleInstance(); + services.ConfigureIdempotency( + hostContext.Configuration.GetSection(IdempotencyConfiguration.Section) + .Get()!.ConnectionString!, + new IdempotencyMigrationsTableInfo(Schema.Import), + new IdempotencyTableInfo(Schema.Import), + loggerFactory); + + builder.RegisterType() + .As() + .AsSelf() + .InstancePerLifetimeScope(); + builder .RegisterModule(new EditModule(hostContext.Configuration)) .RegisterModule(new BackOfficeModule(hostContext.Configuration, services, loggerFactory, ServiceLifetime.Transient)); diff --git a/src/ParcelRegistry.Consumer.Address.Console/appsettings.json b/src/ParcelRegistry.Consumer.Address.Console/appsettings.json index ae773a67..1ce8dbbe 100644 --- a/src/ParcelRegistry.Consumer.Address.Console/appsettings.json +++ b/src/ParcelRegistry.Consumer.Address.Console/appsettings.json @@ -7,6 +7,10 @@ "BackOffice": "Server=(localdb)\\mssqllocaldb;Database=EFProviders.InMemory.ParcelRegistry;Trusted_Connection=True;TrustServerCertificate=True;" }, + "Idempotency": { + "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFProviders.InMemory.ParcelRegistry;Trusted_Connection=True;TrustServerCertificate=True;" + }, + "Kafka": { "BootstrapServers": "localhost:29092/" }, diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 80ccd1ca..3ad02e32 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -5,6 +5,7 @@ namespace ParcelRegistry.Consumer.Address.Projections using System.Threading; using System.Threading.Tasks; using Api.BackOffice.Abstractions; + using Be.Vlaanderen.Basisregisters.CommandHandling.Idempotency; using Be.Vlaanderen.Basisregisters.GrAr.Contracts.AddressRegistry; using Be.Vlaanderen.Basisregisters.GrAr.Provenance; using Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector; @@ -198,9 +199,18 @@ await DetachBecauseRemoved( foreach (var command in commandByParcels) { - await commandHandler.Handle(command, ct); + try + { + await commandHandler.HandleIdempotent(command, ct); + } + catch (IdempotencyException) + { + // do nothing + } } + await backOfficeContext.Database.BeginTransactionAsync(); + foreach (var parcelId in commandByParcels.Select(x => x.ParcelId)) { var parcel = await parcels.GetAsync(new ParcelStreamId(parcelId), ct); @@ -226,6 +236,8 @@ await DetachBecauseRemoved( await backOfficeContext.AddIdempotentParcelAddressRelation(parcelId, addressPersistentLocalId, ct); } } + + await backOfficeContext.Database.CommitTransactionAsync(); }); When(async (commandHandler, message, ct) => diff --git a/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs b/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs index b2ff47e9..0aaf132b 100644 --- a/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs +++ b/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs @@ -1,5 +1,6 @@ -namespace ParcelRegistry.Projections.BackOffice +namespace ParcelRegistry.Projections.BackOffice { + using System.Threading.Tasks; using Api.BackOffice.Abstractions; using Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; @@ -107,41 +108,10 @@ await backOfficeContext.AddIdempotentParcelAddressRelation( } }); - When>(async (_, message, cancellationToken) => + When>((_, message, cancellationToken) => { - await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken); - - foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) - { - var relation = await backOfficeContext.FindParcelAddressRelation( - new ParcelId(message.Message.ParcelId), - new AddressPersistentLocalId(addressPersistentLocalId), - cancellationToken); - - if (relation is not null) - { - await backOfficeContext.RemoveIdempotentParcelAddressRelation( - new ParcelId(message.Message.ParcelId), - new AddressPersistentLocalId(addressPersistentLocalId), - cancellationToken); - } - } - - foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) - { - var relation = await backOfficeContext.FindParcelAddressRelation( - new ParcelId(message.Message.ParcelId), - new AddressPersistentLocalId(addressPersistentLocalId), - cancellationToken); - - if (relation is null) - { - await backOfficeContext.AddIdempotentParcelAddressRelation( - new ParcelId(message.Message.ParcelId), - new AddressPersistentLocalId(addressPersistentLocalId), - cancellationToken); - } - } + // Do nothing + return Task.CompletedTask; }); } } diff --git a/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs index efe64fd0..e6a4fd1b 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests-Readdress.cs @@ -1,4 +1,4 @@ -namespace ParcelRegistry.Tests.ProjectionTests.BackOffice +namespace ParcelRegistry.Tests.ProjectionTests.BackOffice { using System.Threading; using System.Threading.Tasks; @@ -91,51 +91,5 @@ await Sut newAddressParcelRelation!.Count.Should().Be(2); }); } - - [Fact] - public async Task GivenParcelAddressesWereReaddressed_ThenAddressesAreAttachedAndDetached() - { - _fixture.Customize(new WithFixedParcelId()); - - var attachedAddressPersistentLocalIds = new[] { 1, 2, 3 }; - var detachedAddressPersistentLocalIds = new[] { 4, 5, 6 }; - - var eventBuilder = new ParcelAddressesWereReaddressedBuilder(_fixture); - - foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) - { - eventBuilder.WithAttachedAddress(addressPersistentLocalId); - } - - foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) - { - await AddRelation(_fixture.Create(), addressPersistentLocalId); - eventBuilder.WithDetachedAddress(addressPersistentLocalId); - } - - var @event = eventBuilder.Build(); - - await Sut - .Given(@event) - .Then(async _ => - { - foreach (var addressPersistentLocalId in attachedAddressPersistentLocalIds) - { - var parcelAddressRelation = await _backOfficeContext.ParcelAddressRelations.FindAsync( - @event.ParcelId, - addressPersistentLocalId); - parcelAddressRelation.Should().NotBeNull(); - parcelAddressRelation!.Count.Should().Be(1); - } - - foreach (var addressPersistentLocalId in detachedAddressPersistentLocalIds) - { - var parcelAddressRelation = await _backOfficeContext.ParcelAddressRelations.FindAsync( - @event.ParcelId, - addressPersistentLocalId); - parcelAddressRelation.Should().BeNull(); - } - }); - } } } diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs index 5c942168..9925aeb6 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests-Readdress.cs @@ -84,7 +84,7 @@ public async Task AttachAndDetachAddressesWhenStreetNameWasReaddressed() await Then(async _ => { _mockCommandHandler.Verify(x => - x.Handle( + x.HandleIdempotent( It.Is(y => y.ParcelId == parcelOneId && y.Readdresses.Count == 2 @@ -97,7 +97,7 @@ await Then(async _ => CancellationToken.None), Times.Once); _mockCommandHandler.Verify(x => - x.Handle( + x.HandleIdempotent( It.Is(y => y.ParcelId == parcelTwoId && y.Readdresses.Count == 1 From f90235716c4c78730edb06ca4edb83f18a614f5d Mon Sep 17 00:00:00 2001 From: jvandaal Date: Fri, 17 May 2024 13:33:15 +0200 Subject: [PATCH 13/13] feat: produce ParcelAddressesWereReaddressed --- paket.dependencies | 20 ++++++------- paket.lock | 30 +++++++++---------- .../Extensions/MessageExtensions.cs | 11 +++++++ .../ProducerMigrateProjections.cs | 5 ++++ .../ProducerProjections.cs | 5 ++++ 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/paket.dependencies b/paket.dependencies index 7dc15eaf..6331fee1 100755 --- a/paket.dependencies +++ b/paket.dependencies @@ -79,16 +79,16 @@ nuget Be.Vlaanderen.Basisregisters.Projector 15.0.0 nuget Be.Vlaanderen.Basisregisters.Crab 4.0.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Common 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.5.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.5.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Common 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.6.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.6.0 nuget Be.Vlaanderen.Basisregisters.MessageHandling.AwsSqs.Simple 5.0.1 nuget Be.Vlaanderen.Basisregisters.MessageHandling.Kafka.Simple 5.0.1 diff --git a/paket.lock b/paket.lock index 8d12dec6..632c6073 100644 --- a/paket.lock +++ b/paket.lock @@ -253,18 +253,18 @@ NUGET Autofac.Extensions.DependencyInjection (>= 9.0) Be.Vlaanderen.Basisregisters.EventHandling (5.0) Be.Vlaanderen.Basisregisters.Generators.Guid.Deterministic (4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.6) Be.Vlaanderen.Basisregisters.AggregateSource (>= 9.0.1) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) NetTopologySuite (>= 2.5) NodaTime (>= 3.1.11) - Be.Vlaanderen.Basisregisters.GrAr.Contracts (21.5) - Be.Vlaanderen.Basisregisters.GrAr.Edit (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Contracts (21.6) + Be.Vlaanderen.Basisregisters.GrAr.Edit (21.6) NetTopologySuite (>= 2.5) - Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.6) Be.Vlaanderen.Basisregisters.Api (>= 21.0) Be.Vlaanderen.Basisregisters.Shaperon (>= 10.0.2) - Be.Vlaanderen.Basisregisters.GrAr.Import (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Import (21.6) Autofac (>= 8.0) Be.Vlaanderen.Basisregisters.AggregateSource.SqlStreamStore (>= 9.0.1) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) @@ -279,31 +279,31 @@ NUGET Serilog (>= 3.1.1) Serilog.Extensions.Logging (>= 8.0) System.Threading.Tasks.Dataflow (>= 8.0) - Be.Vlaanderen.Basisregisters.GrAr.Legacy (21.5) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Legacy (21.6) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.6) Be.Vlaanderen.Basisregisters.Utilities.Rfc3339DateTimeOffset (>= 4.0) Newtonsoft.Json (>= 13.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.6) AWSSDK.SimpleNotificationService (>= 3.7.301.3) System.Text.Json (>= 8.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.6) Be.Vlaanderen.Basisregisters.AspNetCore.Mvc.Formatters.Json (>= 5.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.6) Be.Vlaanderen.Basisregisters.Utilities.Rfc3339DateTimeOffset (>= 4.0) Microsoft.Extensions.Configuration (>= 8.0) Microsoft.Extensions.Http.Polly (>= 8.0.3) Newtonsoft.Json (>= 13.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.6) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) Be.Vlaanderen.Basisregisters.Crab (>= 4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.6) Microsoft.CSharp (>= 4.7) - Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.6) Be.Vlaanderen.Basisregisters.Auth.AcmIdm (>= 2.0) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) Be.Vlaanderen.Basisregisters.Crab (>= 4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.5) - Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.5) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.6) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.6) Microsoft.CSharp (>= 4.7) Be.Vlaanderen.Basisregisters.MessageHandling.AwsSqs.Simple (5.0.1) AWSSDK.Core (>= 3.7.302.15) diff --git a/src/ParcelRegistry.Producer/Extensions/MessageExtensions.cs b/src/ParcelRegistry.Producer/Extensions/MessageExtensions.cs index c32fca9e..4c88662a 100644 --- a/src/ParcelRegistry.Producer/Extensions/MessageExtensions.cs +++ b/src/ParcelRegistry.Producer/Extensions/MessageExtensions.cs @@ -1,5 +1,6 @@ namespace ParcelRegistry.Producer.Extensions { + using System.Linq; using Be.Vlaanderen.Basisregisters.GrAr.Provenance; using Contracts = Be.Vlaanderen.Basisregisters.GrAr.Contracts.ParcelRegistry; using Legacy = Legacy.Events; @@ -64,6 +65,16 @@ public static Contracts.ParcelAddressWasReplacedBecauseAddressWasReaddressed ToC message.PreviousAddressPersistentLocalId, message.Provenance.ToContract()); + public static Contracts.ParcelAddressesWereReaddressed ToContract(this ParcelAggregate.ParcelAddressesWereReaddressed message) => + new Contracts.ParcelAddressesWereReaddressed( + message.ParcelId.ToString("D"), + message.CaPaKey, + message.AttachedAddressPersistentLocalIds, + message.DetachedAddressPersistentLocalIds, + message.AddressRegistryReaddresses.Select(x => + new Contracts.AddressRegistryReaddress(x.SourceAddressPersistentLocalId, x.SourceAddressPersistentLocalId)), + message.Provenance.ToContract()); + public static Contracts.ParcelWasMigrated ToContract(this ParcelAggregate.ParcelWasMigrated message) => new Contracts.ParcelWasMigrated( message.OldParcelId.ToString("D"), diff --git a/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs b/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs index 844a5f9c..ca6de405 100644 --- a/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs +++ b/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs @@ -52,6 +52,11 @@ public ProducerMigrateProjections(IProducer producer) await Produce(message.Message.ParcelId, message.Message.ToContract(), message.Position, ct); }); + When>(async (_, message, ct) => + { + await Produce(message.Message.ParcelId, message.Message.ToContract(), message.Position, ct); + }); + When>(async (_, message, ct) => { await Produce(message.Message.ParcelId, message.Message.ToContract(), message.Position, ct); diff --git a/src/ParcelRegistry.Producer/ProducerProjections.cs b/src/ParcelRegistry.Producer/ProducerProjections.cs index 3969213b..31f54aa7 100644 --- a/src/ParcelRegistry.Producer/ProducerProjections.cs +++ b/src/ParcelRegistry.Producer/ProducerProjections.cs @@ -106,6 +106,11 @@ public ProducerProjections(IProducer producer) await Produce(message.Message.ParcelId, message.Message.ToContract(), message.Position, ct); }); + When>(async (_, message, ct) => + { + await Produce(message.Message.ParcelId, message.Message.ToContract(), message.Position, ct); + }); + When>(async (_, message, ct) => { await Produce(message.Message.ParcelId, message.Message.ToContract(), message.Position, ct);