From 59f9d1d6c5a367542fb2d0c2f09971ce60e48387 Mon Sep 17 00:00:00 2001 From: Rik De Peuter Date: Tue, 23 Jul 2024 13:53:17 +0200 Subject: [PATCH 1/7] feat: add command ReplaceParcelAddressBecauseOfMunicipalityMerger --- paket.dependencies | 20 ++--- paket.lock | 30 +++---- .../Parcel/AddressCommandHandlerModule.cs | 12 +++ ...arcelAddressBecauseOfMunicipalityMerger.cs | 47 ++++++++++ ...sWasReplacedBecauseOfMunicipalityMerger.cs | 73 ++++++++++++++++ src/ParcelRegistry/Parcel/Parcel_Address.cs | 16 ++++ src/ParcelRegistry/Parcel/Parcel_State.cs | 15 ++++ .../GivenParcelExists.cs | 86 +++++++++++++++++++ ...lacedBecauseOfMunicipalityMergerBuilder.cs | 62 +++++++++++++ 9 files changed, 336 insertions(+), 25 deletions(-) create mode 100644 src/ParcelRegistry/Parcel/Commands/ReplaceParcelAddressBecauseOfMunicipalityMerger.cs create mode 100644 src/ParcelRegistry/Parcel/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs create mode 100644 test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAddressBecauseOfMunicipalityMerger/GivenParcelExists.cs create mode 100644 test/ParcelRegistry.Tests/Builders/ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder.cs diff --git a/paket.dependencies b/paket.dependencies index 59e7c9af..6972018e 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.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.7.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.7.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Common 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.12.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 22c648cb..9a4fd635 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.7) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) 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.7) - Be.Vlaanderen.Basisregisters.GrAr.Edit (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Contracts (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Edit (21.12) NetTopologySuite (>= 2.5) - Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.12) Be.Vlaanderen.Basisregisters.Api (>= 21.0) Be.Vlaanderen.Basisregisters.Shaperon (>= 10.0.2) - Be.Vlaanderen.Basisregisters.GrAr.Import (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Import (21.12) 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.7) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Legacy (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) Be.Vlaanderen.Basisregisters.Utilities.Rfc3339DateTimeOffset (>= 4.0) Newtonsoft.Json (>= 13.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.12) AWSSDK.SimpleNotificationService (>= 3.7.301.3) System.Text.Json (>= 8.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.12) Be.Vlaanderen.Basisregisters.AspNetCore.Mvc.Formatters.Json (>= 5.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) 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.7) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.12) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) Be.Vlaanderen.Basisregisters.Crab (>= 4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) Microsoft.CSharp (>= 4.7) - Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.12) 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.7) - Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.7) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.12) Microsoft.CSharp (>= 4.7) Be.Vlaanderen.Basisregisters.MessageHandling.AwsSqs.Simple (5.0.1) AWSSDK.Core (>= 3.7.302.15) diff --git a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs index c0c9f85d..36d665b0 100644 --- a/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs +++ b/src/ParcelRegistry/Parcel/AddressCommandHandlerModule.cs @@ -93,6 +93,18 @@ public AddressCommandHandlerModule( parcel.ReaddressAddresses(message.Command.Readdresses); }); + + 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.ReplaceAddressBecauseOfMunicipalityMerger(message.Command.NewAddressPersistentLocalId, message.Command.PreviousAddressPersistentLocalId); + }); } } } diff --git a/src/ParcelRegistry/Parcel/Commands/ReplaceParcelAddressBecauseOfMunicipalityMerger.cs b/src/ParcelRegistry/Parcel/Commands/ReplaceParcelAddressBecauseOfMunicipalityMerger.cs new file mode 100644 index 00000000..eb5b76ab --- /dev/null +++ b/src/ParcelRegistry/Parcel/Commands/ReplaceParcelAddressBecauseOfMunicipalityMerger.cs @@ -0,0 +1,47 @@ +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; + + public class ReplaceParcelAddressBecauseOfMunicipalityMerger : IHasCommandProvenance + { + private static readonly Guid Namespace = new Guid("8c9fb5a8-9629-4798-b927-a89d07f7ae86"); + public ParcelId ParcelId { get; } + public AddressPersistentLocalId NewAddressPersistentLocalId { get; } + public AddressPersistentLocalId PreviousAddressPersistentLocalId { get; } + public Provenance Provenance { get; } + + public ReplaceParcelAddressBecauseOfMunicipalityMerger( + ParcelId parcelId, + AddressPersistentLocalId newAddressPersistentLocalId, + AddressPersistentLocalId previousAddressPersistentLocalId, + Provenance provenance) + { + ParcelId = parcelId; + NewAddressPersistentLocalId = newAddressPersistentLocalId; + PreviousAddressPersistentLocalId = previousAddressPersistentLocalId; + Provenance = provenance; + } + + public Guid CreateCommandId() + => Deterministic.Create(Namespace, $"ReplaceParcelAddressBecauseOfMunicipalityMerger-{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/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs new file mode 100644 index 00000000..90815a3a --- /dev/null +++ b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs @@ -0,0 +1,73 @@ +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)] + [EventName(EventName)] + [EventDescription("Het adres werd gekoppeld aan het perceel.")] //TODO-rik event description + public sealed class ParcelAddressWasReplacedBecauseOfMunicipalityMerger : IParcelEvent + { + public const string EventName = "ParcelAddressWasReplacedBecauseOfMunicipalityMerger"; // 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 nieuwe adres.")] + public int NewAddressPersistentLocalId { get; } + + [EventPropertyDescription("Objectidentificator van het vorige adres.")] + public int PreviousAddressPersistentLocalId { get; } + + [EventPropertyDescription("Metadata bij het event.")] + public ProvenanceData Provenance { get; private set; } + + public ParcelAddressWasReplacedBecauseOfMunicipalityMerger( + ParcelId parcelId, + VbrCaPaKey vbrCaPaKey, + AddressPersistentLocalId newAddressPersistentLocalId, + AddressPersistentLocalId previousAddressPersistentLocalId) + { + ParcelId = parcelId; + NewAddressPersistentLocalId = newAddressPersistentLocalId; + PreviousAddressPersistentLocalId = previousAddressPersistentLocalId; + CaPaKey = vbrCaPaKey; + } + + [JsonConstructor] + private ParcelAddressWasReplacedBecauseOfMunicipalityMerger( + Guid parcelId, + string caPaKey, + int newAddressPersistentLocalId, + int previousAddressPersistentLocalId, + ProvenanceData provenance) + : this( + new ParcelId(parcelId), + new VbrCaPaKey(caPaKey), + new AddressPersistentLocalId(newAddressPersistentLocalId), + new AddressPersistentLocalId(previousAddressPersistentLocalId)) + => ((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(NewAddressPersistentLocalId.ToString()); + fields.Add(PreviousAddressPersistentLocalId.ToString()); + return fields; + } + + public string GetHash() => this.ToEventHash(EventName); + } +} diff --git a/src/ParcelRegistry/Parcel/Parcel_Address.cs b/src/ParcelRegistry/Parcel/Parcel_Address.cs index 398aee8e..b45114ab 100644 --- a/src/ParcelRegistry/Parcel/Parcel_Address.cs +++ b/src/ParcelRegistry/Parcel/Parcel_Address.cs @@ -114,5 +114,21 @@ public void ReaddressAddresses( addressPersistentLocalIdsToDetach, readdresses.Select(x => new AddressRegistryReaddress(x)))); } + + public void ReplaceAddressBecauseOfMunicipalityMerger( + AddressPersistentLocalId newAddressPersistentLocalId, + AddressPersistentLocalId previousAddressPersistentLocalId) + { + if (!AddressPersistentLocalIds.Contains(previousAddressPersistentLocalId)) + { + return; + } + + ApplyChange(new ParcelAddressWasReplacedBecauseOfMunicipalityMerger( + ParcelId, + CaPaKey, + newAddressPersistentLocalId, + previousAddressPersistentLocalId)); + } } } diff --git a/src/ParcelRegistry/Parcel/Parcel_State.cs b/src/ParcelRegistry/Parcel/Parcel_State.cs index 557ec900..002718bd 100644 --- a/src/ParcelRegistry/Parcel/Parcel_State.cs +++ b/src/ParcelRegistry/Parcel/Parcel_State.cs @@ -24,6 +24,7 @@ public partial class Parcel public bool IsRemoved { get; private set; } public string LastEventHash => _lastEvent is null ? _lastSnapshotEventHash : _lastEvent.GetHash(); + public ProvenanceData LastProvenanceData => _lastEvent is null ? _lastSnapshotProvenance : _lastEvent.Provenance; @@ -49,6 +50,7 @@ private Parcel() Register(When); Register(When); Register(When); + Register(When); Register(When); } @@ -162,6 +164,19 @@ private void When(ParcelAddressesWereReaddressed @event) _lastEvent = @event; } + private void When(ParcelAddressWasReplacedBecauseOfMunicipalityMerger @event) + { + _addressPersistentLocalIds.RemoveAll(x => x == new AddressPersistentLocalId(@event.PreviousAddressPersistentLocalId)); + + var newAddressPersistentLocalId = new AddressPersistentLocalId(@event.NewAddressPersistentLocalId); + if (!_addressPersistentLocalIds.Contains(newAddressPersistentLocalId)) + { + _addressPersistentLocalIds.Add(new AddressPersistentLocalId(@event.NewAddressPersistentLocalId)); + } + + _lastEvent = @event; + } + private void When(ParcelSnapshotV2 @event) { ParcelId = new ParcelId(@event.ParcelId); diff --git a/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAddressBecauseOfMunicipalityMerger/GivenParcelExists.cs b/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAddressBecauseOfMunicipalityMerger/GivenParcelExists.cs new file mode 100644 index 00000000..3ada4532 --- /dev/null +++ b/test/ParcelRegistry.Tests/AggregateTests/WhenReplacingAddressBecauseOfMunicipalityMerger/GivenParcelExists.cs @@ -0,0 +1,86 @@ +namespace ParcelRegistry.Tests.AggregateTests.WhenReplacingAddressBecauseOfMunicipalityMerger +{ + using System.Collections.Generic; + using System.Linq; + using Autofac; + using Be.Vlaanderen.Basisregisters.AggregateSource.Snapshotting; + using FluentAssertions; + using ParcelRegistry.Parcel; + using ParcelRegistry.Tests.Builders; + using ParcelRegistry.Tests.Fixtures; + 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 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 ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder(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 ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder(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()); + } + } +} diff --git a/test/ParcelRegistry.Tests/Builders/ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder.cs b/test/ParcelRegistry.Tests/Builders/ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder.cs new file mode 100644 index 00000000..90cbc2d4 --- /dev/null +++ b/test/ParcelRegistry.Tests/Builders/ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder.cs @@ -0,0 +1,62 @@ +namespace ParcelRegistry.Tests.Builders +{ + using AutoFixture; + using EventExtensions; + using Parcel; + using Parcel.Events; + + public class ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder + { + private readonly Fixture _fixture; + private ParcelId? _parcelId; + private AddressPersistentLocalId? _newAddressPersistentLocalId; + private AddressPersistentLocalId? _previousAddressPersistentLocalId; + private VbrCaPaKey? _vbrCaPaKey; + + public ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder(Fixture fixture) + { + _fixture = fixture; + } + + public ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder WithParcelId(ParcelId parcelId) + { + _parcelId = parcelId; + + return this; + } + + public ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder WithVbrCaPaKey(VbrCaPaKey vbrCaPaKey) + { + _vbrCaPaKey = vbrCaPaKey; + + return this; + } + + public ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder WithNewAddress(int address) + { + _newAddressPersistentLocalId = new AddressPersistentLocalId(address); + + return this; + } + + public ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder WithPreviousAddress(int address) + { + _previousAddressPersistentLocalId = new AddressPersistentLocalId(address); + + return this; + } + + public ParcelAddressWasReplacedBecauseOfMunicipalityMerger Build() + { + var ParcelAddressWasReplacedBecauseOfMunicipalityMerger = new ParcelAddressWasReplacedBecauseOfMunicipalityMerger( + _parcelId ?? _fixture.Create(), + _vbrCaPaKey ?? _fixture.Create(), + _newAddressPersistentLocalId ?? _fixture.Create(), + _previousAddressPersistentLocalId ?? _fixture.Create()); + + ParcelAddressWasReplacedBecauseOfMunicipalityMerger.SetFixtureProvenance(_fixture); + + return ParcelAddressWasReplacedBecauseOfMunicipalityMerger; + } + } +} From f03471df7943405b0b2b34f330482015109c5396 Mon Sep 17 00:00:00 2001 From: jvandaal Date: Tue, 23 Jul 2024 15:21:09 +0200 Subject: [PATCH 2/7] feat: consume address municipality merger events for AddressConsumerItems --- .../Projections/BackofficeKafkaProjection.cs | 25 ++++++ .../AddressConsumerKafkaProjectionTests.cs | 79 +++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/ParcelRegistry.Consumer.Address/Projections/BackofficeKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/BackofficeKafkaProjection.cs index 4f90610f..c73f5d53 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/BackofficeKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/BackofficeKafkaProjection.cs @@ -50,6 +50,19 @@ await context , ct); }); + When(async (context, message, ct) => + { + await context + .AddressConsumerItems + .AddAsync(new AddressConsumerItem( + message.AddressPersistentLocalId, + AddressStatus.Proposed, + message.GeometryMethod, + message.GeometrySpecification, + ParsePosition(message.ExtendedWkbGeometry)) + , ct); + }); + When(async (context, message, ct) => { var address = await context.AddressConsumerItems.FindAsync(message.AddressPersistentLocalId, cancellationToken: ct); @@ -86,6 +99,12 @@ await context address!.Status = AddressStatus.Rejected; }); + When(async (context, message, ct) => + { + var address = await context.AddressConsumerItems.FindAsync(message.AddressPersistentLocalId, cancellationToken: ct); + address!.Status = AddressStatus.Rejected; + }); + When(async (context, message, ct) => { var address = await context.AddressConsumerItems.FindAsync(message.AddressPersistentLocalId, cancellationToken: ct); @@ -110,6 +129,12 @@ await context address!.Status = AddressStatus.Retired; }); + When(async (context, message, ct) => + { + var address = await context.AddressConsumerItems.FindAsync(message.AddressPersistentLocalId, cancellationToken: ct); + address!.Status = AddressStatus.Retired; + }); + When(async (context, message, ct) => { var address = await context.AddressConsumerItems.FindAsync(message.AddressPersistentLocalId, cancellationToken: ct); diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/AddressConsumerKafkaProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/AddressConsumerKafkaProjectionTests.cs index f379d715..fb879518 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/AddressConsumerKafkaProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/AddressConsumerKafkaProjectionTests.cs @@ -97,6 +97,29 @@ await context.AddressConsumerItems.FindAsync( }); } + [Fact] + public async Task AddressWasProposedForMunicipalityMerger_AddsAddress() + { + var addressWasProposed = Fixture.Create(); + + Given(addressWasProposed); + + await Then(async context => + { + var address = + await context.AddressConsumerItems.FindAsync( + addressWasProposed.AddressPersistentLocalId); + + address.Should().NotBeNull(); + address!.AddressId.Should().BeNull(); + address.IsRemoved.Should().Be(false); + address.Status.Should().Be(AddressStatus.Proposed); + address.GeometryMethod.Should().Be(addressWasProposed.GeometryMethod); + address.GeometrySpecification.Should().Be(addressWasProposed.GeometrySpecification); + address.Position.Should().Be((Point)_wkbReader.Read(addressWasProposed.ExtendedWkbGeometry.ToByteArray())); + }); + } + [Fact] public async Task AddressWasApproved_UpdatesStatusAddress() { @@ -324,6 +347,31 @@ await context.AddressConsumerItems.FindAsync( }); } + [Fact] + public async Task AddressWasRejectedBecauseOfMunicipalityMerger_UpdatesStatusAddress() + { + var addressWasProposedV2 = CreateAddressWasProposedV2(); + var addressWasRejected = Fixture.Build() + .FromFactory(() => new AddressWasRejectedBecauseOfMunicipalityMerger( + addressWasProposedV2.StreetNamePersistentLocalId, + addressWasProposedV2.AddressPersistentLocalId, + Fixture.Create(), + Fixture.Create())) + .Create(); + + Given(addressWasProposedV2, addressWasRejected); + + await Then(async context => + { + var address = + await context.AddressConsumerItems.FindAsync( + addressWasProposedV2.AddressPersistentLocalId); + + address.Should().NotBeNull(); + address!.Status.Should().Be(AddressStatus.Rejected); + }); + } + [Fact] public async Task AddressWasCorrectedFromRejectedToProposed_UpdatesStatusAddress() { @@ -474,6 +522,37 @@ await context.AddressConsumerItems.FindAsync( }); } + [Fact] + public async Task AddressWasRetiredBecauseOfMunicipalityMerger_UpdatesStatusAddress() + { + var addressWasProposedV2 = CreateAddressWasProposedV2(); + var addressWasApproved = Fixture.Build() + .FromFactory(() => new AddressWasApproved( + addressWasProposedV2.StreetNamePersistentLocalId, + addressWasProposedV2.AddressPersistentLocalId, + Fixture.Create())) + .Create(); + var addressWasRetired = Fixture.Build() + .FromFactory(() => new AddressWasRetiredBecauseOfMunicipalityMerger( + addressWasProposedV2.StreetNamePersistentLocalId, + addressWasProposedV2.AddressPersistentLocalId, + Fixture.Create(), + Fixture.Create())) + .Create(); + + Given(addressWasProposedV2, addressWasApproved, addressWasRetired); + + await Then(async context => + { + var address = + await context.AddressConsumerItems.FindAsync( + addressWasProposedV2.AddressPersistentLocalId); + + address.Should().NotBeNull(); + address!.Status.Should().Be(AddressStatus.Retired); + }); + } + [Fact] public async Task AddressWasRemovedBecauseStreetNameWasRemoved() { From 331f494186a1e5f09dd0e43ad17980d47924d5bf Mon Sep 17 00:00:00 2001 From: jvandaal Date: Tue, 23 Jul 2024 15:28:24 +0200 Subject: [PATCH 3/7] feat: consume AddressWasRejectedBecauseOfMunicipalityMerger and AddressWasRetiredBecauseOfMunicipalityMerger and send domain commands --- .../CommandHandlingKafkaProjection.cs | 60 +++++++++++++ .../CommandHandlingKafkaProjectionTests.cs | 87 +++++++++++++++++-- 2 files changed, 142 insertions(+), 5 deletions(-) diff --git a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs index 4f239b3b..b0bb89cc 100644 --- a/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs +++ b/src/ParcelRegistry.Consumer.Address/Projections/CommandHandlingKafkaProjection.cs @@ -99,6 +99,36 @@ await DetachBecauseRejected( ct); }); + When(async (commandHandler, message, ct) => + { + await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); + var relations = backOfficeContext.ParcelAddressRelations + .AsNoTracking() + .Where(x => x.AddressPersistentLocalId == new AddressPersistentLocalId(message.AddressPersistentLocalId)) + .ToList(); + + foreach (var relation in relations) + { + var command = new ReplaceParcelAddressBecauseOfMunicipalityMerger( + new ParcelId(relation.ParcelId), + new AddressPersistentLocalId(message.NewAddressPersistentLocalId), + new AddressPersistentLocalId(message.AddressPersistentLocalId), + FromProvenance(message.Provenance)); + + await commandHandler.Handle(command, ct); + + await backOfficeContext.RemoveIdempotentParcelAddressRelation( + command.ParcelId, + command.PreviousAddressPersistentLocalId, + ct); + + await backOfficeContext.AddIdempotentParcelAddressRelation( + command.ParcelId, + command.NewAddressPersistentLocalId, + ct); + } + }); + When(async (commandHandler, message, ct) => { await DetachBecauseRetired( @@ -135,6 +165,36 @@ await DetachBecauseRetired( ct); }); + When(async (commandHandler, message, ct) => + { + await using var backOfficeContext = await _backOfficeContextFactory.CreateDbContextAsync(ct); + var relations = backOfficeContext.ParcelAddressRelations + .AsNoTracking() + .Where(x => x.AddressPersistentLocalId == new AddressPersistentLocalId(message.AddressPersistentLocalId)) + .ToList(); + + foreach (var relation in relations) + { + var command = new ReplaceParcelAddressBecauseOfMunicipalityMerger( + new ParcelId(relation.ParcelId), + new AddressPersistentLocalId(message.NewAddressPersistentLocalId), + new AddressPersistentLocalId(message.AddressPersistentLocalId), + FromProvenance(message.Provenance)); + + await commandHandler.Handle(command, ct); + + await backOfficeContext.RemoveIdempotentParcelAddressRelation( + command.ParcelId, + command.PreviousAddressPersistentLocalId, + ct); + + await backOfficeContext.AddIdempotentParcelAddressRelation( + command.ParcelId, + command.NewAddressPersistentLocalId, + ct); + } + }); + When(async (commandHandler, message, ct) => { await DetachBecauseRemoved( diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs index 8c1e2120..c5f3de9d 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Consumer.Address/CommandHandlingKafkaProjectionTests.cs @@ -1,17 +1,13 @@ 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 FluentAssertions; using Microsoft.EntityFrameworkCore; @@ -20,7 +16,6 @@ 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; @@ -587,6 +582,88 @@ await Then(async _ => }); } + [Fact] + public async Task ReplaceParcelAddressBecauseOfMunicipalityMerger_AddressWasRejectedBecauseOfMunicipalityMerger() + { + var oldAddressPersistentLocalId = 1; + var newAddressPersistentLocalId = 2; + + var @event = new AddressWasRejectedBecauseOfMunicipalityMerger( + Fixture.Create(), + oldAddressPersistentLocalId, + newAddressPersistentLocalId, + new Provenance( + Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), + Application.ParcelRegistry.ToString(), + Modification.Update.ToString(), + Organisation.Aiv.ToString(), + "test")); + + AddParcelAddressRelations(Fixture.Create(), [oldAddressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [oldAddressPersistentLocalId]); + + Given(@event); + await Then(async _ => + { + _mockCommandHandler.Verify(x => x.Handle( + It.IsAny(), CancellationToken.None), + Times.Exactly(2)); + + var oldAddressRelations = _fakeBackOfficeContext.ParcelAddressRelations + .Where(x => x.AddressPersistentLocalId == oldAddressPersistentLocalId) + .ToList(); + + var newAddressRelations = _fakeBackOfficeContext.ParcelAddressRelations + .Where(x => x.AddressPersistentLocalId == newAddressPersistentLocalId) + .ToList(); + + oldAddressRelations.Should().BeEmpty(); + newAddressRelations.Should().HaveCount(2); + await Task.CompletedTask; + }); + } + + [Fact] + public async Task ReplaceParcelAddressBecauseOfMunicipalityMerger_AddressWasRetiredBecauseOfMunicipalityMerger() + { + var oldAddressPersistentLocalId = 1; + var newAddressPersistentLocalId = 2; + + var @event = new AddressWasRetiredBecauseOfMunicipalityMerger( + Fixture.Create(), + oldAddressPersistentLocalId, + newAddressPersistentLocalId, + new Provenance( + Instant.FromDateTimeOffset(DateTimeOffset.Now).ToString(), + Application.ParcelRegistry.ToString(), + Modification.Update.ToString(), + Organisation.Aiv.ToString(), + "test")); + + AddParcelAddressRelations(Fixture.Create(), [oldAddressPersistentLocalId]); + AddParcelAddressRelations(Fixture.Create(), [oldAddressPersistentLocalId]); + + Given(@event); + await Then(async _ => + { + _mockCommandHandler.Verify(x => x.Handle( + It.IsAny(), CancellationToken.None), + Times.Exactly(2)); + + var oldAddressRelations = _fakeBackOfficeContext.ParcelAddressRelations + .Where(x => x.AddressPersistentLocalId == oldAddressPersistentLocalId) + .ToList(); + + var newAddressRelations = _fakeBackOfficeContext.ParcelAddressRelations + .Where(x => x.AddressPersistentLocalId == newAddressPersistentLocalId) + .ToList(); + + oldAddressRelations.Should().BeEmpty(); + newAddressRelations.Should().HaveCount(2); + await Task.CompletedTask; + }); + } + private void AddParcelAddressRelations(ParcelId parcelId, int[] addressPersistentLocalIds) { foreach (var addressPersistentLocalId in addressPersistentLocalIds) From 7be2a2b348f793bd4761607678dae3e7a90784b0 Mon Sep 17 00:00:00 2001 From: Rik De Peuter Date: Tue, 23 Jul 2024 16:43:58 +0200 Subject: [PATCH 4/7] feat: add projections for ParcelAddressWasReplacedBecauseOfMunicipalityMerger --- paket.dependencies | 20 +-- paket.lock | 30 ++--- .../ProducerProjections.cs | 14 +++ .../Extensions/MessageExtensions.cs | 3 + .../ProducerMigrateProjections.cs | 5 + .../BackOfficeProjections.cs | 21 ++++ .../ParcelExtract/ParcelExtractProjections.cs | 11 ++ .../ParcelLinkExtractProjections.cs | 21 ++++ .../ParcelLatestItemProjections.cs | 62 ++++++---- .../ParcelVersion/ParcelVersionProjections.cs | 84 +++++++++---- .../LastChangedListProjections.cs | 5 + .../ParcelDetail/ParcelDetailProjections.cs | 116 ++++++++---------- .../ParcelSyndicationProjections.cs | 13 ++ ...sWasReplacedBecauseOfMunicipalityMerger.cs | 2 +- .../ParcelBackOfficeProjectionsTests.cs | 44 +++++++ .../ParcelLatestItemProjectionTests.cs | 24 ++++ .../ParcelVersionProjectionTests.cs | 52 ++++++++ .../Legacy/ParcelDetailItemV2Tests.cs | 39 ++++++ .../Legacy/ParcelSyndicationV2Tests.cs | 48 ++++++++ 19 files changed, 475 insertions(+), 139 deletions(-) diff --git a/paket.dependencies b/paket.dependencies index 6972018e..28883309 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.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.12.0 -nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.12.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Common 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Notifications 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Contracts 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Edit 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Import 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Legacy 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Extracts 21.14.0 +nuget Be.Vlaanderen.Basisregisters.GrAr.Oslo 21.14.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 9a4fd635..b27247b5 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.12) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.14) 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.12) - Be.Vlaanderen.Basisregisters.GrAr.Edit (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Contracts (21.14) + Be.Vlaanderen.Basisregisters.GrAr.Edit (21.14) NetTopologySuite (>= 2.5) - Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Extracts (21.14) Be.Vlaanderen.Basisregisters.Api (>= 21.0) Be.Vlaanderen.Basisregisters.Shaperon (>= 10.0.2) - Be.Vlaanderen.Basisregisters.GrAr.Import (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Import (21.14) 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.12) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Legacy (21.14) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.14) Be.Vlaanderen.Basisregisters.Utilities.Rfc3339DateTimeOffset (>= 4.0) Newtonsoft.Json (>= 13.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Notifications (21.14) AWSSDK.SimpleNotificationService (>= 3.7.301.3) System.Text.Json (>= 8.0.3) - Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Oslo (21.14) Be.Vlaanderen.Basisregisters.AspNetCore.Mvc.Formatters.Json (>= 5.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.14) 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.12) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.14) Be.Vlaanderen.Basisregisters.CommandHandling (>= 9.0.1) Be.Vlaanderen.Basisregisters.Crab (>= 4.0) - Be.Vlaanderen.Basisregisters.GrAr.Common (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.14) Microsoft.CSharp (>= 4.7) - Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Provenance.AcmIdm (21.14) 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.12) - Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.12) + Be.Vlaanderen.Basisregisters.GrAr.Common (21.14) + Be.Vlaanderen.Basisregisters.GrAr.Provenance (21.14) 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.Snapshot.Oslo/ProducerProjections.cs b/src/ParcelRegistry.Producer.Snapshot.Oslo/ProducerProjections.cs index ba186986..289c9bdc 100644 --- a/src/ParcelRegistry.Producer.Snapshot.Oslo/ProducerProjections.cs +++ b/src/ParcelRegistry.Producer.Snapshot.Oslo/ProducerProjections.cs @@ -69,6 +69,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.Producer/Extensions/MessageExtensions.cs b/src/ParcelRegistry.Producer/Extensions/MessageExtensions.cs index 1d806eb3..a7b9ead2 100644 --- a/src/ParcelRegistry.Producer/Extensions/MessageExtensions.cs +++ b/src/ParcelRegistry.Producer/Extensions/MessageExtensions.cs @@ -45,6 +45,9 @@ public static Contracts.ParcelWasRegistered ToContract(this Legacy.ParcelWasRegi public static Contracts.ParcelAddressWasAttachedV2 ToContract(this ParcelAggregate.ParcelAddressWasAttachedV2 message) => new Contracts.ParcelAddressWasAttachedV2(message.ParcelId.ToString("D"), message.CaPaKey, message.AddressPersistentLocalId, message.Provenance.ToContract()); + public static Contracts.ParcelAddressWasReplacedBecauseOfMunicipalityMerger ToContract(this ParcelAggregate.ParcelAddressWasReplacedBecauseOfMunicipalityMerger message) => + new Contracts.ParcelAddressWasReplacedBecauseOfMunicipalityMerger(message.ParcelId.ToString("D"), message.CaPaKey, message.NewAddressPersistentLocalId, message.PreviousAddressPersistentLocalId, message.Provenance.ToContract()); + public static Contracts.ParcelAddressWasDetachedV2 ToContract(this ParcelAggregate.ParcelAddressWasDetachedV2 message) => new Contracts.ParcelAddressWasDetachedV2(message.ParcelId.ToString("D"), message.CaPaKey, message.AddressPersistentLocalId, message.Provenance.ToContract()); diff --git a/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs b/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs index ca6de405..92d97f01 100644 --- a/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs +++ b/src/ParcelRegistry.Producer/ProducerMigrateProjections.cs @@ -27,6 +27,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.Projections.BackOffice/BackOfficeProjections.cs b/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs index 79c316e7..1a46fe2a 100644 --- a/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs +++ b/src/ParcelRegistry.Projections.BackOffice/BackOfficeProjections.cs @@ -47,6 +47,27 @@ await backOfficeContext.AddIdempotentParcelAddressRelation( await backOfficeContext.SaveChangesAsync(cancellationToken); }); + When>(async (_, message, cancellationToken) => + { + await DelayProjection(message, delayInSeconds, cancellationToken); + + await using var backOfficeContext = await backOfficeContextFactory.CreateDbContextAsync(cancellationToken); + + await backOfficeContext.RemoveIdempotentParcelAddressRelation( + new ParcelId(message.Message.ParcelId), + new AddressPersistentLocalId(message.Message.PreviousAddressPersistentLocalId), + cancellationToken, + saveChanges: false); + + await backOfficeContext.AddIdempotentParcelAddressRelation( + new ParcelId(message.Message.ParcelId), + new AddressPersistentLocalId(message.Message.NewAddressPersistentLocalId), + cancellationToken, + saveChanges: false); + + await backOfficeContext.SaveChangesAsync(cancellationToken); + }); + When>(async (_, message, cancellationToken) => { await DelayProjection(message, delayInSeconds, cancellationToken); diff --git a/src/ParcelRegistry.Projections.Extract/ParcelExtract/ParcelExtractProjections.cs b/src/ParcelRegistry.Projections.Extract/ParcelExtract/ParcelExtractProjections.cs index e0f0be18..a049ee05 100644 --- a/src/ParcelRegistry.Projections.Extract/ParcelExtract/ParcelExtractProjections.cs +++ b/src/ParcelRegistry.Projections.Extract/ParcelExtract/ParcelExtractProjections.cs @@ -54,6 +54,17 @@ await context.FindAndUpdateParcelExtract( ct); }); + When>(async (context, message, ct) => + { + await context.FindAndUpdateParcelExtract( + message.Message.ParcelId, + parcel => + { + UpdateVersie(parcel, message.Message.Provenance.Timestamp); + }, + ct); + }); + When>(async (context, message, ct) => { await context.FindAndUpdateParcelExtract( diff --git a/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs b/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs index fe066d06..211a6b1b 100644 --- a/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs +++ b/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs @@ -68,6 +68,27 @@ await context }, ct); }); + When>(async (context, message, ct) => + { + await RemoveParcelLink(context, message.Message.ParcelId, message.Message.PreviousAddressPersistentLocalId, ct); + + await context + .ParcelLinkExtract + .AddAsync(new ParcelLinkExtractItem + { + ParcelId = message.Message.ParcelId, + CaPaKey = message.Message.CaPaKey, + AddressPersistentLocalId = message.Message.NewAddressPersistentLocalId, + Count = 1, + DbaseRecord = new ParcelLinkDbaseRecord + { + objecttype = { Value = ParcelObjectType }, + adresobjid = { Value = message.Message.CaPaKey }, + adresid = { Value = message.Message.NewAddressPersistentLocalId } + }.ToBytes(_encoding) + }, ct); + }); + When>(async (context, message, ct) => { await RemoveParcelLink(context, message.Message.ParcelId, message.Message.AddressPersistentLocalId, ct); diff --git a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs index 4809549f..5e5fed7a 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs @@ -1,5 +1,8 @@ namespace ParcelRegistry.Projections.Integration.ParcelLatestItem { + using System; + using System.Threading; + using System.Threading.Tasks; using Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; using Converters; @@ -104,6 +107,18 @@ await context message.Message.CaPaKey), ct); }); + When>(async (context, message, ct) => + { + await RemoveParcelAddress(context, message.Message.ParcelId, message.Message.PreviousAddressPersistentLocalId, ct); + + await context + .ParcelLatestItemAddresses + .AddAsync(new ParcelLatestItemAddress( + message.Message.ParcelId, + message.Message.NewAddressPersistentLocalId, + message.Message.CaPaKey), ct); + }); + When>(async (context, message, ct) => { var previousAddress = await context @@ -142,14 +157,7 @@ await context { 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); - } + await RemoveParcelAddress(context, message.Message.ParcelId, addressPersistentLocalId, ct); } foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) @@ -172,39 +180,39 @@ await context.ParcelLatestItemAddresses.AddAsync( When>(async (context, message, ct) => { - var latestItemAddress = await context - .ParcelLatestItemAddresses - .FindAsync(new object?[] { message.Message.ParcelId, message.Message.AddressPersistentLocalId }, cancellationToken: ct); - - context.ParcelLatestItemAddresses.Remove(latestItemAddress); + await RemoveParcelAddress(context, message.Message.ParcelId, message.Message.AddressPersistentLocalId, ct); }); When>(async (context, message, ct) => { - var latestItemAddress = await context - .ParcelLatestItemAddresses - .FindAsync(new object?[] { message.Message.ParcelId, message.Message.AddressPersistentLocalId }, cancellationToken: ct); - - context.ParcelLatestItemAddresses.Remove(latestItemAddress); + await RemoveParcelAddress(context, message.Message.ParcelId, message.Message.AddressPersistentLocalId, ct); }); When>(async (context, message, ct) => { - var latestItemAddress = await context - .ParcelLatestItemAddresses - .FindAsync(new object?[] { message.Message.ParcelId, message.Message.AddressPersistentLocalId }, cancellationToken: ct); - - context.ParcelLatestItemAddresses.Remove(latestItemAddress); + await RemoveParcelAddress(context, message.Message.ParcelId, message.Message.AddressPersistentLocalId, ct); }); When>(async (context, message, ct) => { - var latestItemAddress = await context - .ParcelLatestItemAddresses - .FindAsync(new object?[] { message.Message.ParcelId, message.Message.AddressPersistentLocalId }, cancellationToken: ct); + await RemoveParcelAddress(context, message.Message.ParcelId, message.Message.AddressPersistentLocalId, ct); + }); + } + private static async Task RemoveParcelAddress( + IntegrationContext context, + Guid parcelId, + int addressPersistentLocalId, + CancellationToken ct) + { + var latestItemAddress = await context + .ParcelLatestItemAddresses + .FindAsync(new object?[] { parcelId, addressPersistentLocalId }, cancellationToken: ct); + + if (latestItemAddress is not null) + { context.ParcelLatestItemAddresses.Remove(latestItemAddress); - }); + } } private static void UpdateVersionTimestamp(ParcelLatestItem parcel, Instant versionTimestamp) diff --git a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs index f75d995b..08108e7b 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs @@ -1,5 +1,8 @@ namespace ParcelRegistry.Projections.Integration.ParcelVersion { + using System; + using System.Threading; + using System.Threading.Tasks; using Be.Vlaanderen.Basisregisters.GrAr.Common; using Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore; @@ -120,6 +123,28 @@ await context message.Message.CaPaKey), ct); }); + When>(async (context, message, ct) => + { + await context.CreateNewParcelVersion( + message.Message.ParcelId, + message, + _ => { }, ct); + + await RemoveParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.PreviousAddressPersistentLocalId, + ct); + + await context + .ParcelVersionAddresses + .AddAsync(new ParcelVersionAddress( + message.Position, + message.Message.ParcelId, + message.Message.NewAddressPersistentLocalId, + message.Message.CaPaKey), ct); + }); + When>(async (context, message, ct) => { await context.CreateNewParcelVersion( @@ -205,12 +230,11 @@ await context.CreateNewParcelVersion( message, _ => { }, ct); - var versionAddress = await context - .ParcelVersionAddresses - .FindAsync(new object?[] { message.Position, message.Message.ParcelId, message.Message.AddressPersistentLocalId }, - cancellationToken: ct); - - context.ParcelVersionAddresses.Remove(versionAddress); + await RemoveParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.AddressPersistentLocalId, + ct); }); When>(async (context, message, ct) => @@ -220,12 +244,11 @@ await context.CreateNewParcelVersion( message, _ => { }, ct); - var versionAddress = await context - .ParcelVersionAddresses - .FindAsync(new object?[] { message.Position, message.Message.ParcelId, message.Message.AddressPersistentLocalId }, - cancellationToken: ct); - - context.ParcelVersionAddresses.Remove(versionAddress); + await RemoveParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.AddressPersistentLocalId, + ct); }); When>(async (context, message, ct) => @@ -235,12 +258,11 @@ await context.CreateNewParcelVersion( message, _ => { }, ct); - var versionAddress = await context - .ParcelVersionAddresses - .FindAsync(new object?[] { message.Position, message.Message.ParcelId, message.Message.AddressPersistentLocalId }, - cancellationToken: ct); - - context.ParcelVersionAddresses.Remove(versionAddress); + await RemoveParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.AddressPersistentLocalId, + ct); }); When>(async (context, message, ct) => @@ -250,12 +272,11 @@ await context.CreateNewParcelVersion( message, _ => { }, ct); - var versionAddress = await context - .ParcelVersionAddresses - .FindAsync(new object?[] { message.Position, message.Message.ParcelId, message.Message.AddressPersistentLocalId }, - cancellationToken: ct); - - context.ParcelVersionAddresses.Remove(versionAddress); + await RemoveParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.AddressPersistentLocalId, + ct); }); #region Legacy @@ -433,5 +454,20 @@ await context.CreateNewParcelVersion( } #endregion + + private static async Task RemoveParcelAddress( + IntegrationContext context, + long position, + Guid parcelId, + int addressPersistentLocalId, + CancellationToken ct) + { + var versionAddress = await context + .ParcelVersionAddresses + .FindAsync(new object?[] { position, parcelId, addressPersistentLocalId }, + cancellationToken: ct); + + context.ParcelVersionAddresses.Remove(versionAddress); + } } } diff --git a/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs b/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs index 959af620..26a17dce 100644 --- a/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs +++ b/src/ParcelRegistry.Projections.LastChangedList/LastChangedListProjections.cs @@ -89,6 +89,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) => { await GetLastChangedRecordsAndUpdatePosition(message.Message.ParcelId.ToString(), message.Position, context, ct); diff --git a/src/ParcelRegistry.Projections.Legacy/ParcelDetail/ParcelDetailProjections.cs b/src/ParcelRegistry.Projections.Legacy/ParcelDetail/ParcelDetailProjections.cs index 8f754dff..547d7890 100644 --- a/src/ParcelRegistry.Projections.Legacy/ParcelDetail/ParcelDetailProjections.cs +++ b/src/ParcelRegistry.Projections.Legacy/ParcelDetail/ParcelDetailProjections.cs @@ -3,6 +3,8 @@ namespace ParcelRegistry.Projections.Legacy.ParcelDetail using System; using System.Collections.Generic; using System.Linq; + using System.Threading; + using System.Threading.Tasks; using Be.Vlaanderen.Basisregisters.EventHandling; using Be.Vlaanderen.Basisregisters.GrAr.Common; using Be.Vlaanderen.Basisregisters.GrAr.Common.NetTopology; @@ -68,14 +70,22 @@ await context.FindAndUpdateParcelDetail( message.Message.ParcelId, entity => { - context.Entry(entity).Collection(x => x.Addresses).Load(); + AddParcelAddress(context, entity, message.Message.AddressPersistentLocalId); - if (!entity.Addresses.Any(parcelAddress => - parcelAddress.AddressPersistentLocalId == message.Message.AddressPersistentLocalId - && parcelAddress.ParcelId == message.Message.ParcelId)) - { - entity.Addresses.Add(new ParcelDetailAddress(message.Message.ParcelId, message.Message.AddressPersistentLocalId)); - } + UpdateHash(entity, message); + UpdateVersionTimestamp(entity, message.Message.Provenance.Timestamp); + }, + ct); + }); + + When>(async (context, message, ct) => + { + await context.FindAndUpdateParcelDetail( + message.Message.ParcelId, + entity => + { + RemoveParcelAddress(context, entity, message.Message.PreviousAddressPersistentLocalId); + AddParcelAddress(context, entity, message.Message.NewAddressPersistentLocalId); UpdateHash(entity, message); UpdateVersionTimestamp(entity, message.Message.Provenance.Timestamp); @@ -89,15 +99,7 @@ await context.FindAndUpdateParcelDetail( message.Message.ParcelId, entity => { - context.Entry(entity).Collection(x => x.Addresses).Load(); - - var addressToRemove = entity.Addresses.SingleOrDefault(parcelAddress => - parcelAddress.AddressPersistentLocalId == message.Message.AddressPersistentLocalId - && parcelAddress.ParcelId == message.Message.ParcelId); - if (addressToRemove is not null) - { - entity.Addresses.Remove(addressToRemove); - } + RemoveParcelAddress(context, entity, message.Message.AddressPersistentLocalId); UpdateHash(entity, message); UpdateVersionTimestamp(entity, message.Message.Provenance.Timestamp); @@ -111,15 +113,7 @@ await context.FindAndUpdateParcelDetail( message.Message.ParcelId, entity => { - context.Entry(entity).Collection(x => x.Addresses).Load(); - - var addressToRemove = entity.Addresses.SingleOrDefault(parcelAddress => - parcelAddress.AddressPersistentLocalId == message.Message.AddressPersistentLocalId - && parcelAddress.ParcelId == message.Message.ParcelId); - if (addressToRemove is not null) - { - entity.Addresses.Remove(addressToRemove); - } + RemoveParcelAddress(context, entity, message.Message.AddressPersistentLocalId); UpdateHash(entity, message); UpdateVersionTimestamp(entity, message.Message.Provenance.Timestamp); @@ -133,15 +127,7 @@ await context.FindAndUpdateParcelDetail( message.Message.ParcelId, entity => { - context.Entry(entity).Collection(x => x.Addresses).Load(); - - var addressToRemove = entity.Addresses.SingleOrDefault(parcelAddress => - parcelAddress.AddressPersistentLocalId == message.Message.AddressPersistentLocalId - && parcelAddress.ParcelId == message.Message.ParcelId); - if (addressToRemove is not null) - { - entity.Addresses.Remove(addressToRemove); - } + RemoveParcelAddress(context, entity, message.Message.AddressPersistentLocalId); UpdateHash(entity, message); UpdateVersionTimestamp(entity, message.Message.Provenance.Timestamp); @@ -155,15 +141,7 @@ await context.FindAndUpdateParcelDetail( message.Message.ParcelId, entity => { - context.Entry(entity).Collection(x => x.Addresses).Load(); - - var addressToRemove = entity.Addresses.SingleOrDefault(parcelAddress => - parcelAddress.AddressPersistentLocalId == message.Message.AddressPersistentLocalId - && parcelAddress.ParcelId == message.Message.ParcelId); - if (addressToRemove is not null) - { - entity.Addresses.Remove(addressToRemove); - } + RemoveParcelAddress(context, entity, message.Message.AddressPersistentLocalId); UpdateHash(entity, message); UpdateVersionTimestamp(entity, message.Message.Provenance.Timestamp); @@ -217,31 +195,14 @@ 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); - } + RemoveParcelAddress(context, entity, addressPersistentLocalId); } foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) { - var relation = entity.Addresses.SingleOrDefault(parcelAddress => - parcelAddress.AddressPersistentLocalId == addressPersistentLocalId - && parcelAddress.ParcelId == message.Message.ParcelId); - - if (relation is null) - { - entity.Addresses.Add(new ParcelDetailAddress(message.Message.ParcelId, addressPersistentLocalId)); - } + AddParcelAddress(context, entity, addressPersistentLocalId); } UpdateHash(entity, message); @@ -320,6 +281,37 @@ await context.FindAndUpdateParcelDetail( }); } + private static void RemoveParcelAddress( + LegacyContext context, + ParcelDetail entity, + int addressPersistentLocalId) + { + context.Entry(entity).Collection(x => x.Addresses).Load(); + + var addressToRemove = entity.Addresses.SingleOrDefault(parcelAddress => + parcelAddress.AddressPersistentLocalId == addressPersistentLocalId + && parcelAddress.ParcelId == entity.ParcelId); + if (addressToRemove is not null) + { + entity.Addresses.Remove(addressToRemove); + } + } + + private static void AddParcelAddress( + LegacyContext context, + ParcelDetail entity, + int addressPersistentLocalId) + { + context.Entry(entity).Collection(x => x.Addresses).Load(); + + if (!entity.Addresses.Any(parcelAddress => + parcelAddress.AddressPersistentLocalId == addressPersistentLocalId + && parcelAddress.ParcelId == entity.ParcelId)) + { + entity.Addresses.Add(new ParcelDetailAddress(entity.ParcelId, addressPersistentLocalId)); + } + } + private static void UpdateHash(ParcelDetail entity, Envelope wrappedEvent) where T : IHaveHash, IMessage { if (!wrappedEvent.Metadata.ContainsKey(AddEventHashPipe.HashMetadataKey)) diff --git a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs index ae2dc595..67429fe8 100755 --- a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs +++ b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs @@ -224,6 +224,19 @@ await context.CreateNewParcelSyndicationItem( ct); }); + When>(async (context, message, ct) => + { + await context.CreateNewParcelSyndicationItem( + message.Message.ParcelId, + message, + x => + { + x.RemoveAddressPersistentLocalId(message.Message.PreviousAddressPersistentLocalId); + x.AddAddressPersistentLocalId(message.Message.NewAddressPersistentLocalId); + }, + ct); + }); + When>(async (context, message, ct) => { await context.CreateNewParcelSyndicationItem( diff --git a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs index 90815a3a..48e42150 100644 --- a/src/ParcelRegistry/Parcel/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs +++ b/src/ParcelRegistry/Parcel/Events/ParcelAddressWasReplacedBecauseOfMunicipalityMerger.cs @@ -10,7 +10,7 @@ namespace ParcelRegistry.Parcel.Events [EventTags(EventTag.For.Sync, EventTag.For.Edit)] [EventName(EventName)] - [EventDescription("Het adres werd gekoppeld aan het perceel.")] //TODO-rik event description + [EventDescription("Het adres werd herkoppeld van het perceel in functie van een gemeentefusie.")] public sealed class ParcelAddressWasReplacedBecauseOfMunicipalityMerger : IParcelEvent { public const string EventName = "ParcelAddressWasReplacedBecauseOfMunicipalityMerger"; // BE CAREFUL CHANGING THIS!! diff --git a/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests.cs index cfec3df1..b35320ab 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/BackOffice/ParcelBackOfficeProjectionsTests.cs @@ -241,6 +241,50 @@ await Sut }); } + [Fact] + public async Task GivenParcelAddressWasReplacedBecauseOfMunicipalityMergerAndPreviousRelationDoesntExist_ThenRelationIsReplaced() + { + var parcelAddressWasReplaced = _fixture.Create(); + + await Sut + .Given(BuildEnvelope(parcelAddressWasReplaced)) + .Then(async _ => + { + var previousRelation = await _backOfficeContext.ParcelAddressRelations.FindAsync( + parcelAddressWasReplaced.ParcelId, parcelAddressWasReplaced.PreviousAddressPersistentLocalId); + + previousRelation.Should().BeNull(); + + var newRelation = await _backOfficeContext.ParcelAddressRelations.FindAsync( + parcelAddressWasReplaced.ParcelId, parcelAddressWasReplaced.NewAddressPersistentLocalId); + + newRelation.Should().NotBeNull(); + newRelation!.ParcelId.Should().Be(parcelAddressWasReplaced.ParcelId); + newRelation.AddressPersistentLocalId.Should().Be(parcelAddressWasReplaced.NewAddressPersistentLocalId); + }); + } + + [Fact] + public async Task GivenParcelAddressWasReplacedBecauseOfMunicipalityMergerAndNewRelationExists_ThenNothing() + { + var parcelAddressWasReplaced = _fixture.Create(); + + var expectedRelation = await AddRelation( + parcelAddressWasReplaced.ParcelId, + parcelAddressWasReplaced.NewAddressPersistentLocalId); + + await Sut + .Given(BuildEnvelope(parcelAddressWasReplaced)) + .Then(async _ => + { + var result = await _backOfficeContext.ParcelAddressRelations.FindAsync( + parcelAddressWasReplaced.ParcelId, parcelAddressWasReplaced.NewAddressPersistentLocalId); + + result.Should().NotBeNull(); + result.Should().BeSameAs(expectedRelation); + }); + } + private async Task AddRelation(Guid parcelId, int addressPersistentLocalId) { return await _backOfficeContext.AddIdempotentParcelAddressRelation( diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests.cs index 6064bb19..17eb674c 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelLatestItemProjectionTests.cs @@ -244,6 +244,30 @@ await Sut }); } + [Fact] + public async Task WhenParcelAddressWasReplacedBecauseOfMunicipalityMerger() + { + var attached = _fixture.Create(); + + var message = new ParcelAddressWasReplacedBecauseOfMunicipalityMerger( + new ParcelId(attached.ParcelId), + new VbrCaPaKey(attached.CaPaKey), + new AddressPersistentLocalId(attached.AddressPersistentLocalId + 1), + new AddressPersistentLocalId(attached.AddressPersistentLocalId)); + + await Sut + .Given(_fixture.Create(), message) + .Then(async context => + { + var previousLatestItem = await context.ParcelLatestItemAddresses.FindAsync(message.ParcelId, message.PreviousAddressPersistentLocalId); + previousLatestItem.Should().BeNull(); + + var newLatestItem = await context.ParcelLatestItemAddresses.FindAsync(message.ParcelId, message.NewAddressPersistentLocalId); + newLatestItem.Should().NotBeNull(); + newLatestItem!.CaPaKey.Should().Be(message.CaPaKey); + }); + } + protected override ParcelLatestItemProjections CreateProjection() => new(new OptionsWrapper(new IntegrationOptions{Namespace = Namespace })); } diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests.cs index 3187e28d..b4f5a699 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Integration/ParcelVersionProjectionTests.cs @@ -493,6 +493,58 @@ await Sut }); } + [Fact] + public async Task WhenParcelAddressWasReplacedBecauseOfMunicipalityMerger() + { + var parcelWasImported = _fixture.Create(); + var attached = _fixture.Create(); + var message = new ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder(_fixture) + .WithPreviousAddress(attached.AddressPersistentLocalId) + .WithNewAddress(attached.AddressPersistentLocalId + 1) + .Build(); + + var position = _fixture.Create(); + var importedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, _fixture.Create() }, + { Envelope.PositionMetadataKey, position }, + { Envelope.EventNameMetadataKey, _fixture.Create()} + }; + var attachedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, _fixture.Create() }, + { Envelope.PositionMetadataKey, position + 1 }, + { Envelope.EventNameMetadataKey, _fixture.Create()} + }; + var messageMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, _fixture.Create() }, + { Envelope.PositionMetadataKey, position + 2 }, + { Envelope.EventNameMetadataKey, "EventName"} + }; + + await Sut + .Given( + new Envelope(new Envelope(parcelWasImported, importedMetadata)), + new Envelope(new Envelope(attached, attachedMetadata)), + new Envelope(new Envelope(message, messageMetadata))) + .Then(async context => + { + var parcelVersions = await context.ParcelVersions.FindAsync(position + 2, message.ParcelId); + parcelVersions.Should().NotBeNull(); + parcelVersions!.Type.Should().Be("EventName"); + + var previousParcelVersionAddress = + await context.ParcelVersionAddresses.FindAsync(position + 2, message.ParcelId, message.PreviousAddressPersistentLocalId); + previousParcelVersionAddress.Should().BeNull(); + + var newParcelVersionAddress = + await context.ParcelVersionAddresses.FindAsync(position + 2, message.ParcelId, message.NewAddressPersistentLocalId); + newParcelVersionAddress.Should().NotBeNull(); + newParcelVersionAddress!.CaPaKey.Should().Be(message.CaPaKey); + }); + } + #region Legacy [Fact] diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests.cs index 9169a3bf..e4be0459 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelDetailItemV2Tests.cs @@ -13,6 +13,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 Fixtures; using FluentAssertions; using Parcel; @@ -233,6 +234,44 @@ await Sut }); } + [Fact] + public async Task WhenParcelAddressWasReplacedBecauseOfMunicipalityMerger() + { + var parcelWasMigrated = _fixture.Create(); + var migratedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, parcelWasMigrated.GetHash() } + }; + + var addressWasReplaced = new ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder(_fixture) + .WithParcelId(new ParcelId(parcelWasMigrated.ParcelId)) + .WithVbrCaPaKey(new VbrCaPaKey(parcelWasMigrated.CaPaKey)) + .WithPreviousAddress(new AddressPersistentLocalId(parcelWasMigrated.AddressPersistentLocalIds.First())) + .WithNewAddress(new AddressPersistentLocalId(parcelWasMigrated.AddressPersistentLocalIds.Max(x => x) + 1)) + .Build(); + var replacedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, addressWasReplaced.GetHash() } + }; + + await Sut + .Given( + new Envelope(new Envelope(parcelWasMigrated, migratedMetadata)), + new Envelope(new Envelope(addressWasReplaced, replacedMetadata))) + .Then(async context => + { + var parcelDetailV2 = await context.ParcelDetails.FindAsync(parcelWasMigrated.ParcelId); + parcelDetailV2.Should().NotBeNull(); + parcelDetailV2!.Addresses.Select(x => x.AddressPersistentLocalId).Should() + .NotContain(addressWasReplaced.PreviousAddressPersistentLocalId); + parcelDetailV2.Addresses.Select(x => x.AddressPersistentLocalId).Should() + .Contain(addressWasReplaced.NewAddressPersistentLocalId); + + parcelDetailV2.VersionTimestamp.Should().Be(addressWasReplaced.Provenance.Timestamp); + parcelDetailV2.LastEventHash.Should().Be(addressWasReplaced.GetHash()); + }); + } + private Envelope CreateEnvelope(TEvent @event) where TEvent : IMessage, IHaveHash { diff --git a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs index 3abf48ea..4d4fb156 100644 --- a/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs +++ b/test/ParcelRegistry.Tests/ProjectionTests/Legacy/ParcelSyndicationV2Tests.cs @@ -383,6 +383,54 @@ await Sut }); } + [Fact] + public async Task GivenParcelAddressWasReplacedBecauseOfMunicipalityMerger_ThenAddressesAreAttachedAndDetached() + { + _fixture.Customizations.Add(new WithUniqueInteger()); + + var parcelWasImported = _fixture.Create(); + var parcelAddressWasAttached = _fixture.Create(); + + var @event = new ParcelAddressWasReplacedBecauseOfMunicipalityMergerBuilder(_fixture) + .WithPreviousAddress(parcelAddressWasAttached.AddressPersistentLocalId) + .WithNewAddress(parcelAddressWasAttached.AddressPersistentLocalId + 1) + .Build(); + + var position = _fixture.Create(); + var parcelWasImportedMetadata = new Dictionary + { + { AddEventHashPipe.HashMetadataKey, parcelWasImported.GetHash() }, + { Envelope.PositionMetadataKey, position }, + { Envelope.EventNameMetadataKey, nameof(ParcelWasImported) }, + }; + var parcelAddressWasAttachedMetadata = 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(ParcelAddressWasReplacedBecauseOfMunicipalityMerger) }, + }; + + await Sut + .Given( + new Envelope(new Envelope(parcelWasImported, parcelWasImportedMetadata)), + new Envelope(new Envelope(parcelAddressWasAttached, parcelAddressWasAttachedMetadata)), + new Envelope(new Envelope(@event, eventMetadata))) + .Then(async context => + { + var parcelSyndicationItem = await context.ParcelSyndication.FindAsync(position); + parcelSyndicationItem.Should().NotBeNull(); + + parcelSyndicationItem!.AddressPersistentLocalIds.Should().Contain(@event.NewAddressPersistentLocalId); + parcelSyndicationItem!.AddressPersistentLocalIds.Should().NotContain(@event.PreviousAddressPersistentLocalId); + }); + } + protected override ParcelSyndicationProjections CreateProjection() => new ParcelSyndicationProjections(); } From 5fcd44ac27f64e500e2af3e0748350d4cc5dcf66 Mon Sep 17 00:00:00 2001 From: jvandaal Date: Tue, 23 Jul 2024 17:15:06 +0200 Subject: [PATCH 5/7] fix: syndication remove all --- .../ParcelSyndication/ParcelSyndication.cs | 7 +++++++ .../ParcelSyndicationProjections.cs | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndication.cs b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndication.cs index 74a4532e..b3085ea6 100755 --- a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndication.cs +++ b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndication.cs @@ -96,6 +96,13 @@ public void AddAddressPersistentLocalId(int addressPersistentLocalId) } } + public void RemoveAllAddressPersistentLocalId(int addressPersistentLocalId) + { + var addressPersistentLocalIds = GetDeserializedAddressPersistentLocalIds(); + addressPersistentLocalIds.RemoveAll(x => x == addressPersistentLocalId); + AddressPersistentLocalIds = addressPersistentLocalIds; + } + public void RemoveAddressPersistentLocalId(int addressPersistentLocalId) { var addressPersistentLocalIds = GetDeserializedAddressPersistentLocalIds(); diff --git a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs index 67429fe8..c690f95b 100755 --- a/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs +++ b/src/ParcelRegistry.Projections.Legacy/ParcelSyndication/ParcelSyndicationProjections.cs @@ -231,7 +231,7 @@ await context.CreateNewParcelSyndicationItem( message, x => { - x.RemoveAddressPersistentLocalId(message.Message.PreviousAddressPersistentLocalId); + x.RemoveAllAddressPersistentLocalId(message.Message.PreviousAddressPersistentLocalId); x.AddAddressPersistentLocalId(message.Message.NewAddressPersistentLocalId); }, ct); @@ -242,7 +242,7 @@ await context.CreateNewParcelSyndicationItem( await context.CreateNewParcelSyndicationItem( message.Message.ParcelId, message, - x => x.RemoveAddressPersistentLocalId(message.Message.AddressPersistentLocalId), + x => x.RemoveAllAddressPersistentLocalId(message.Message.AddressPersistentLocalId), ct); }); @@ -251,7 +251,7 @@ await context.CreateNewParcelSyndicationItem( await context.CreateNewParcelSyndicationItem( message.Message.ParcelId, message, - x => x.RemoveAddressPersistentLocalId(message.Message.AddressPersistentLocalId), + x => x.RemoveAllAddressPersistentLocalId(message.Message.AddressPersistentLocalId), ct); }); @@ -260,7 +260,7 @@ await context.CreateNewParcelSyndicationItem( await context.CreateNewParcelSyndicationItem( message.Message.ParcelId, message, - x => x.RemoveAddressPersistentLocalId(message.Message.AddressPersistentLocalId), + x => x.RemoveAllAddressPersistentLocalId(message.Message.AddressPersistentLocalId), ct); }); @@ -269,7 +269,7 @@ await context.CreateNewParcelSyndicationItem( await context.CreateNewParcelSyndicationItem( message.Message.ParcelId, message, - x => x.RemoveAddressPersistentLocalId(message.Message.AddressPersistentLocalId), + x => x.RemoveAllAddressPersistentLocalId(message.Message.AddressPersistentLocalId), ct); }); @@ -295,7 +295,7 @@ await context.CreateNewParcelSyndicationItem( { foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) { - x.RemoveAddressPersistentLocalId(addressPersistentLocalId); + x.RemoveAllAddressPersistentLocalId(addressPersistentLocalId); } foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) From 3db3c6832d369ecdec4821c70b138df8484103e7 Mon Sep 17 00:00:00 2001 From: Rik De Peuter Date: Tue, 23 Jul 2024 17:21:54 +0200 Subject: [PATCH 6/7] fix: correctly add link for ParcelAddressesWereReaddressed --- .../ParcelLinkExtractProjections.cs | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs b/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs index 211a6b1b..a19859ef 100644 --- a/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs +++ b/src/ParcelRegistry.Projections.Extract/ParcelLinkExtract/ParcelLinkExtractProjections.cs @@ -71,22 +71,7 @@ await context When>(async (context, message, ct) => { await RemoveParcelLink(context, message.Message.ParcelId, message.Message.PreviousAddressPersistentLocalId, ct); - - await context - .ParcelLinkExtract - .AddAsync(new ParcelLinkExtractItem - { - ParcelId = message.Message.ParcelId, - CaPaKey = message.Message.CaPaKey, - AddressPersistentLocalId = message.Message.NewAddressPersistentLocalId, - Count = 1, - DbaseRecord = new ParcelLinkDbaseRecord - { - objecttype = { Value = ParcelObjectType }, - adresobjid = { Value = message.Message.CaPaKey }, - adresid = { Value = message.Message.NewAddressPersistentLocalId } - }.ToBytes(_encoding) - }, ct); + await AddParcelLink(context, message.Message.ParcelId, message.Message.CaPaKey, message.Message.NewAddressPersistentLocalId, ct); }); When>(async (context, message, ct) => @@ -156,26 +141,12 @@ await context { foreach (var addressPersistentLocalId in message.Message.DetachedAddressPersistentLocalIds) { - var relation = await context - .ParcelLinkExtract - .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); - - if (relation is not null) - { - context.ParcelLinkExtract.Remove(relation); - } + await RemoveParcelLink(context, message.Message.ParcelId, addressPersistentLocalId, ct); } foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) { - var relation = await context - .ParcelLinkExtract - .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); - - if (relation is not null) - { - await context.ParcelLinkExtract.AddAsync(relation, ct); - } + await AddParcelLink(context, message.Message.ParcelId, message.Message.CaPaKey, addressPersistentLocalId, ct); } }); } @@ -190,7 +161,41 @@ private static async Task RemoveParcelLink( .ParcelLinkExtract .FindAsync(new object?[] { parcelId, addressPersistentLocalId }, ct); - context.Remove(linkExtractItem!); + if (linkExtractItem is not null) + { + context.Remove(linkExtractItem); + } + } + + private async Task AddParcelLink( + ExtractContext context, + Guid parcelId, + string caPaKey, + int addressPersistentLocalId, + CancellationToken ct) + { + var newAddress = await context + .ParcelLinkExtract + .FindAsync(new object?[] { parcelId, addressPersistentLocalId }, ct); + + if (newAddress is null || context.Entry(newAddress).State == EntityState.Deleted) + { + await context + .ParcelLinkExtract + .AddAsync(new ParcelLinkExtractItem + { + ParcelId = parcelId, + CaPaKey = caPaKey, + AddressPersistentLocalId = addressPersistentLocalId, + Count = 1, + DbaseRecord = new ParcelLinkDbaseRecord + { + objecttype = { Value = ParcelObjectType }, + adresobjid = { Value = caPaKey }, + adresid = { Value = addressPersistentLocalId } + }.ToBytes(_encoding) + }, ct); + } } } } From c6e0afbff632b0dc4ebbfd693d98819f1b251f47 Mon Sep 17 00:00:00 2001 From: Rik De Peuter Date: Tue, 23 Jul 2024 17:22:25 +0200 Subject: [PATCH 7/7] chore: check if address link exists before adding in projections --- .../ParcelLatestItemProjections.cs | 51 +++++------ .../ParcelVersion/ParcelVersionProjections.cs | 91 +++++++++++-------- 2 files changed, 78 insertions(+), 64 deletions(-) diff --git a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs index 5e5fed7a..d9624d02 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelLatestItem/ParcelLatestItemProjections.cs @@ -99,24 +99,13 @@ await context.FindAndUpdateParcel( When>(async (context, message, ct) => { - await context - .ParcelLatestItemAddresses - .AddAsync(new ParcelLatestItemAddress( - message.Message.ParcelId, - message.Message.AddressPersistentLocalId, - message.Message.CaPaKey), ct); + await AddParcelAddress(context, message.Message.ParcelId, message.Message.CaPaKey, message.Message.AddressPersistentLocalId, ct); }); When>(async (context, message, ct) => { await RemoveParcelAddress(context, message.Message.ParcelId, message.Message.PreviousAddressPersistentLocalId, ct); - - await context - .ParcelLatestItemAddresses - .AddAsync(new ParcelLatestItemAddress( - message.Message.ParcelId, - message.Message.NewAddressPersistentLocalId, - message.Message.CaPaKey), ct); + await AddParcelAddress(context, message.Message.ParcelId, message.Message.CaPaKey, message.Message.NewAddressPersistentLocalId, ct); }); When>(async (context, message, ct) => @@ -162,19 +151,7 @@ await context foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) { - var relation = await context - .ParcelLatestItemAddresses - .FindAsync([message.Message.ParcelId, addressPersistentLocalId], ct); - - if (relation is null) - { - await context.ParcelLatestItemAddresses.AddAsync( - new ParcelLatestItemAddress( - message.Message.ParcelId, - addressPersistentLocalId, - message.Message.CaPaKey), - ct); - } + await AddParcelAddress(context, message.Message.ParcelId, message.Message.CaPaKey, addressPersistentLocalId, ct); } }); @@ -215,6 +192,28 @@ private static async Task RemoveParcelAddress( } } + private static async Task AddParcelAddress( + IntegrationContext context, + Guid parcelId, + string caPaKey, + int addressPersistentLocalId, + CancellationToken ct) + { + var newAddress = await context + .ParcelLatestItemAddresses + .FindAsync([parcelId, addressPersistentLocalId], cancellationToken: ct); + + if (newAddress is null || context.Entry(newAddress).State == EntityState.Deleted) + { + await context + .ParcelLatestItemAddresses + .AddAsync(new ParcelLatestItemAddress( + parcelId, + addressPersistentLocalId, + caPaKey), ct); + } + } + private static void UpdateVersionTimestamp(ParcelLatestItem parcel, Instant versionTimestamp) => parcel.VersionTimestamp = versionTimestamp; } diff --git a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs index 08108e7b..e8b1222e 100644 --- a/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs +++ b/src/ParcelRegistry.Projections.Integration/ParcelVersion/ParcelVersionProjections.cs @@ -9,6 +9,7 @@ using Converters; using Infrastructure; using Legacy.Events; + using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Parcel; using Parcel.Events; @@ -114,13 +115,12 @@ await context.CreateNewParcelVersion( message, _ => { }, ct); - await context - .ParcelVersionAddresses - .AddAsync(new ParcelVersionAddress( - message.Position, - message.Message.ParcelId, - message.Message.AddressPersistentLocalId, - message.Message.CaPaKey), ct); + await AddParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.CaPaKey, + message.Message.AddressPersistentLocalId, + ct); }); When>(async (context, message, ct) => @@ -136,13 +136,12 @@ await RemoveParcelAddress(context, message.Message.PreviousAddressPersistentLocalId, ct); - await context - .ParcelVersionAddresses - .AddAsync(new ParcelVersionAddress( - message.Position, - message.Message.ParcelId, - message.Message.NewAddressPersistentLocalId, - message.Message.CaPaKey), ct); + await AddParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.CaPaKey, + message.Message.NewAddressPersistentLocalId, + ct); }); When>(async (context, message, ct) => @@ -169,7 +168,7 @@ await context.CreateNewParcelVersion( .ParcelVersionAddresses .FindAsync([message.Position, message.Message.ParcelId, message.Message.NewAddressPersistentLocalId], cancellationToken: ct); - if (newAddress is null) + if (newAddress is null || context.Entry(newAddress).State == EntityState.Deleted) { await context .ParcelVersionAddresses @@ -194,32 +193,21 @@ await context.CreateNewParcelVersion( 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); - } + await RemoveParcelAddress(context, + message.Position, + message.Message.ParcelId, + addressPersistentLocalId, + ct); } foreach (var addressPersistentLocalId in message.Message.AttachedAddressPersistentLocalIds) { - var relation = await context - .ParcelVersionAddresses - .FindAsync([message.Position, message.Message.ParcelId, addressPersistentLocalId], ct); - - if (relation is null) - { - await context.ParcelVersionAddresses.AddAsync( - new ParcelVersionAddress( - message.Position, - message.Message.ParcelId, - addressPersistentLocalId, - message.Message.CaPaKey), - ct); - } + await AddParcelAddress(context, + message.Position, + message.Message.ParcelId, + message.Message.CaPaKey, + addressPersistentLocalId, + ct); } }); @@ -455,6 +443,30 @@ await context.CreateNewParcelVersion( #endregion + private static async Task AddParcelAddress( + IntegrationContext context, + long position, + Guid parcelId, + string caPaKey, + int addressPersistentLocalId, + CancellationToken ct) + { + var newAddress = await context + .ParcelVersionAddresses + .FindAsync([position, parcelId, addressPersistentLocalId], cancellationToken: ct); + + if (newAddress is null || context.Entry(newAddress).State == EntityState.Deleted) + { + await context + .ParcelVersionAddresses + .AddAsync(new ParcelVersionAddress( + position, + parcelId, + addressPersistentLocalId, + caPaKey), ct); + } + } + private static async Task RemoveParcelAddress( IntegrationContext context, long position, @@ -467,7 +479,10 @@ private static async Task RemoveParcelAddress( .FindAsync(new object?[] { position, parcelId, addressPersistentLocalId }, cancellationToken: ct); - context.ParcelVersionAddresses.Remove(versionAddress); + if (versionAddress is not null) + { + context.ParcelVersionAddresses.Remove(versionAddress); + } } } }