From 10644150e80a42a51d7064fbb4fa8de0537421a0 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 12 Sep 2017 23:31:06 +0200 Subject: [PATCH] Event migration. --- .../Schemas/FieldRegistry.cs | 13 ++-- .../Contents/Old/ContentArchived.cs | 10 ++- .../Contents/Old/ContentPublished.cs | 10 ++- .../Contents/Old/ContentRestored.cs | 10 ++- .../Contents/Old/ContentUnpublished.cs | 10 ++- .../Schemas/Old/WebhookAdded.cs | 7 +- .../Schemas/Old/WebhookDeleted.cs | 7 +- .../MongoContentRepository_EventHandling.cs | 47 ------------- .../Contents/ContentHistoryEventsCreator.cs | 11 --- .../Schemas/SchemaEvents.cs | 10 +++ .../CQRS/Events/EventDataFormatter.cs | 22 ++++-- .../CQRS/Events/IMigratedEvent.cs | 15 ++++ .../CQRS/Events/NoopEvent.cs | 14 ++++ .../Contents/ContentEventTests.cs | 70 +++++++++++++++++++ .../Contents/ContentVersionLoaderTests.cs | 12 ++-- .../Schemas/SchemaEventTests.cs | 36 ++++++++++ .../TestHelpers/AssertHelper.cs | 18 ++++- .../DefaultDomainObjectRepositoryTests.cs | 16 ++--- .../CQRS/Events/EventDataFormatterTests.cs | 49 +++++++++++-- .../CQRS/Events/EventReceiverTests.cs | 8 +-- 20 files changed, 292 insertions(+), 103 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Write/Schemas/SchemaEvents.cs create mode 100644 src/Squidex.Infrastructure/CQRS/Events/IMigratedEvent.cs create mode 100644 src/Squidex.Infrastructure/CQRS/Events/NoopEvent.cs create mode 100644 tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs create mode 100644 tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaEventTests.cs diff --git a/src/Squidex.Domain.Apps.Core/Schemas/FieldRegistry.cs b/src/Squidex.Domain.Apps.Core/Schemas/FieldRegistry.cs index aedfb23cb..eda134a3a 100644 --- a/src/Squidex.Domain.Apps.Core/Schemas/FieldRegistry.cs +++ b/src/Squidex.Domain.Apps.Core/Schemas/FieldRegistry.cs @@ -59,10 +59,6 @@ namespace Squidex.Domain.Apps.Core.Schemas (id, name, partitioning, properties) => new StringField(id, name, partitioning, (StringFieldProperties)properties)); - Add( - (id, name, partitioning, properties) => - new DateTimeField(id, name, partitioning, (DateTimeFieldProperties)properties)); - Add( (id, name, partitioning, properties) => new JsonField(id, name, partitioning, (JsonFieldProperties)properties)); @@ -71,15 +67,20 @@ namespace Squidex.Domain.Apps.Core.Schemas (id, name, partitioning, properties) => new AssetsField(id, name, partitioning, (AssetsFieldProperties)properties)); + Add( + (id, name, partitioning, properties) => + new GeolocationField(id, name, partitioning, (GeolocationFieldProperties)properties)); + Add( (id, name, partitioning, properties) => new ReferencesField(id, name, partitioning, (ReferencesFieldProperties)properties)); - Add( + Add( (id, name, partitioning, properties) => - new GeolocationField(id, name, partitioning, (GeolocationFieldProperties)properties)); + new DateTimeField(id, name, partitioning, (DateTimeFieldProperties)properties)); typeNameRegistry.MapObsolete(typeof(ReferencesFieldProperties), "DateTime"); + typeNameRegistry.MapObsolete(typeof(DateTimeFieldProperties), "References"); } diff --git a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentArchived.cs b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentArchived.cs index 58fe37ca7..5cd9b3eb9 100644 --- a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentArchived.cs +++ b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentArchived.cs @@ -7,13 +7,19 @@ // ========================================================================== using System; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.Reflection; -namespace Squidex.Domain.Apps.Events.Contents +namespace Squidex.Domain.Apps.Events.Contents.Old { [EventType(nameof(ContentArchived))] [Obsolete] - public sealed class ContentArchived : ContentEvent + public sealed class ContentArchived : ContentEvent, IMigratedEvent { + public IEvent Migrate() + { + return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Archived }); + } } } diff --git a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentPublished.cs b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentPublished.cs index 9ade43e8c..5cba7a414 100644 --- a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentPublished.cs +++ b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentPublished.cs @@ -7,13 +7,19 @@ // ========================================================================== using System; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.Reflection; -namespace Squidex.Domain.Apps.Events.Contents +namespace Squidex.Domain.Apps.Events.Contents.Old { [EventType(nameof(ContentPublished))] [Obsolete] - public sealed class ContentPublished : ContentEvent + public sealed class ContentPublished : ContentEvent, IMigratedEvent { + public IEvent Migrate() + { + return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Published }); + } } } diff --git a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentRestored.cs b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentRestored.cs index 0ddfc09ec..53df84ef6 100644 --- a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentRestored.cs +++ b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentRestored.cs @@ -7,13 +7,19 @@ // ========================================================================== using System; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.Reflection; -namespace Squidex.Domain.Apps.Events.Contents +namespace Squidex.Domain.Apps.Events.Contents.Old { [EventType(nameof(ContentRestored))] [Obsolete] - public sealed class ContentRestored : ContentEvent + public sealed class ContentRestored : ContentEvent, IMigratedEvent { + public IEvent Migrate() + { + return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Draft }); + } } } diff --git a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentUnpublished.cs b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentUnpublished.cs index 32d6986b9..8ef8430de 100644 --- a/src/Squidex.Domain.Apps.Events/Contents/Old/ContentUnpublished.cs +++ b/src/Squidex.Domain.Apps.Events/Contents/Old/ContentUnpublished.cs @@ -7,13 +7,19 @@ // ========================================================================== using System; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.Reflection; -namespace Squidex.Domain.Apps.Events.Contents +namespace Squidex.Domain.Apps.Events.Contents.Old { [EventType(nameof(ContentUnpublished))] [Obsolete] - public sealed class ContentUnpublished : ContentEvent + public sealed class ContentUnpublished : ContentEvent, IMigratedEvent { + public IEvent Migrate() + { + return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Draft }); + } } } diff --git a/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs b/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs index 39926a737..ae1123dc2 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs @@ -13,12 +13,17 @@ namespace Squidex.Domain.Apps.Events.Schemas.Old { [EventType(nameof(WebhookAdded))] [Obsolete] - public sealed class WebhookAdded : SchemaEvent + public sealed class WebhookAdded : SchemaEvent, IMigratedEvent { public Guid Id { get; set; } public Uri Url { get; set; } public string SharedSecret { get; set; } + + public IEvent Migrate() + { + return new NoopEvent(); + } } } diff --git a/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs b/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs index df94a1e4c..14b506c5f 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs @@ -13,8 +13,13 @@ namespace Squidex.Domain.Apps.Events.Schemas.Old { [EventType(nameof(WebhookDeleted))] [Obsolete] - public sealed class WebhookDeleted : SchemaEvent + public sealed class WebhookDeleted : SchemaEvent, IMigratedEvent { public Guid Id { get; set; } + + public IEvent Migrate() + { + return new NoopEvent(); + } } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs index 8ceefa833..cf6e11b2a 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs @@ -9,7 +9,6 @@ using System; using System.Threading.Tasks; using MongoDB.Driver; -using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Contents; @@ -18,8 +17,6 @@ using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Reflection; -#pragma warning disable CS0612 // Type or member is obsolete - namespace Squidex.Domain.Apps.Read.MongoDb.Contents { public partial class MongoContentRepository @@ -95,39 +92,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents }); } - protected Task On(ContentPublished @event, EnvelopeHeaders headers) - { - return ForAppIdAsync(@event.AppId.Id, collection => - { - return collection.UpdateAsync(@event, headers, x => - { - x.Status = Status.Published; - }); - }); - } - - protected Task On(ContentUnpublished @event, EnvelopeHeaders headers) - { - return ForAppIdAsync(@event.AppId.Id, collection => - { - return collection.UpdateAsync(@event, headers, x => - { - x.Status = Status.Draft; - }); - }); - } - - protected Task On(ContentArchived @event, EnvelopeHeaders headers) - { - return ForAppIdAsync(@event.AppId.Id, collection => - { - return collection.UpdateAsync(@event, headers, x => - { - x.Status = Status.Archived; - }); - }); - } - protected Task On(ContentStatusChanged @event, EnvelopeHeaders headers) { return ForAppIdAsync(@event.AppId.Id, collection => @@ -139,17 +103,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents }); } - protected Task On(ContentRestored @event, EnvelopeHeaders headers) - { - return ForAppIdAsync(@event.AppId.Id, collection => - { - return collection.UpdateAsync(@event, headers, x => - { - x.Status = Status.Draft; - }); - }); - } - protected Task On(AssetDeleted @event, EnvelopeHeaders headers) { return ForAppIdAsync(@event.AppId.Id, collection => diff --git a/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs index 45d02810c..6ffea3aea 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs @@ -12,8 +12,6 @@ using Squidex.Domain.Apps.Read.History; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; -#pragma warning disable CS0612 // Type or member is obsolete - namespace Squidex.Domain.Apps.Read.Contents { public sealed class ContentHistoryEventsCreator : HistoryEventsCreatorBase @@ -30,15 +28,6 @@ namespace Squidex.Domain.Apps.Read.Contents AddEventMessage( "deleted content item."); - AddEventMessage( - "restored content item."); - - AddEventMessage( - "published content item."); - - AddEventMessage( - "unpublished content item."); - AddEventMessage( "change status of content item to {[Status]}."); } diff --git a/src/Squidex.Domain.Apps.Write/Schemas/SchemaEvents.cs b/src/Squidex.Domain.Apps.Write/Schemas/SchemaEvents.cs new file mode 100644 index 000000000..1148449e7 --- /dev/null +++ b/src/Squidex.Domain.Apps.Write/Schemas/SchemaEvents.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Squidex.Domain.Apps.Write.Schemas +{ + public class SchemaEvents + { + } +} diff --git a/src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs b/src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs index f3df50a5c..60d6f7095 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs @@ -25,21 +25,33 @@ namespace Squidex.Infrastructure.CQRS.Events this.serializerSettings = serializerSettings ?? new JsonSerializerSettings(); } - public virtual Envelope Parse(EventData eventData) + public virtual Envelope Parse(EventData eventData, bool migrate = true) { var headers = ReadJson(eventData.Metadata); var eventType = typeNameRegistry.GetType(eventData.Type); - var eventContent = ReadJson(eventData.Payload, eventType); + var eventPayload = ReadJson(eventData.Payload, eventType); - var envelope = new Envelope(eventContent, headers); + if (migrate && eventPayload is IMigratedEvent migratedEvent) + { + eventPayload = migratedEvent.Migrate(); + } + + var envelope = new Envelope(eventPayload, headers); return envelope; } - public virtual EventData ToEventData(Envelope envelope, Guid commitId) + public virtual EventData ToEventData(Envelope envelope, Guid commitId, bool migrate = true) { - var eventType = typeNameRegistry.GetName(envelope.Payload.GetType()); + var eventPayload = envelope.Payload; + + if (migrate && eventPayload is IMigratedEvent migratedEvent) + { + eventPayload = migratedEvent.Migrate(); + } + + var eventType = typeNameRegistry.GetName(eventPayload.GetType()); envelope.SetCommitId(commitId); diff --git a/src/Squidex.Infrastructure/CQRS/Events/IMigratedEvent.cs b/src/Squidex.Infrastructure/CQRS/Events/IMigratedEvent.cs new file mode 100644 index 000000000..24534e5a8 --- /dev/null +++ b/src/Squidex.Infrastructure/CQRS/Events/IMigratedEvent.cs @@ -0,0 +1,15 @@ +// ========================================================================== +// IMigratedEvent.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +namespace Squidex.Infrastructure.CQRS.Events +{ + public interface IMigratedEvent + { + IEvent Migrate(); + } +} diff --git a/src/Squidex.Infrastructure/CQRS/Events/NoopEvent.cs b/src/Squidex.Infrastructure/CQRS/Events/NoopEvent.cs new file mode 100644 index 000000000..be6c125f0 --- /dev/null +++ b/src/Squidex.Infrastructure/CQRS/Events/NoopEvent.cs @@ -0,0 +1,14 @@ +// ========================================================================== +// NoopEvent.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +namespace Squidex.Infrastructure.CQRS.Events +{ + public sealed class NoopEvent : IEvent + { + } +} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs new file mode 100644 index 000000000..dcdfe4fee --- /dev/null +++ b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs @@ -0,0 +1,70 @@ +// ========================================================================== +// SchemaEventTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Events.Contents; +using Squidex.Domain.Apps.Events.Contents.Old; +using Squidex.Domain.Apps.Write.TestHelpers; +using Squidex.Infrastructure; +using Xunit; + +#pragma warning disable CS0612 // Type or member is obsolete + +namespace Squidex.Domain.Apps.Write.Contents +{ + public class ContentEventTests + { + private readonly RefToken actor = new RefToken("User", Guid.NewGuid().ToString()); + private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); + private readonly NamedId schemaId = new NamedId(Guid.NewGuid(), "my-schema"); + private readonly Guid contentId = Guid.NewGuid(); + + [Fact] + public void Should_migrate_content_published_to_content_status_changed() + { + var source = CreateEvent(new ContentPublished()); + + source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Published })); + } + + [Fact] + public void Should_migrate_content_unpublished_to_content_status_changed() + { + var source = CreateEvent(new ContentUnpublished()); + + source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft })); + } + + [Fact] + public void Should_migrate_content_restored_to_content_status_changed() + { + var source = CreateEvent(new ContentRestored()); + + source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft })); + } + + [Fact] + public void Should_migrate_content_archived_to_content_status_changed() + { + var source = CreateEvent(new ContentArchived()); + + source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Archived })); + } + + private T CreateEvent(T contentEvent) where T : ContentEvent + { + contentEvent.Actor = actor; + contentEvent.AppId = appId; + contentEvent.SchemaId = schemaId; + contentEvent.ContentId = contentId; + + return contentEvent; + } + } +} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs index 89540bf99..3c6d9a2e0 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs @@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Write.Contents A.CallTo(() => eventStore.GetEventsAsync(streamName)) .Returns(events); - A.CallTo(() => formatter.Parse(eventData1)) + A.CallTo(() => formatter.Parse(eventData1, true)) .Returns(new Envelope(event1)); await Assert.ThrowsAsync(() => sut.LoadAsync(appId, id, 0)); @@ -100,9 +100,9 @@ namespace Squidex.Domain.Apps.Write.Contents A.CallTo(() => eventStore.GetEventsAsync(streamName)) .Returns(events); - A.CallTo(() => formatter.Parse(eventData1)) + A.CallTo(() => formatter.Parse(eventData1, true)) .Returns(new Envelope(event1)); - A.CallTo(() => formatter.Parse(eventData2)) + A.CallTo(() => formatter.Parse(eventData2, true)) .Returns(new Envelope(event2)); var data = await sut.LoadAsync(appId, id, 3); @@ -131,11 +131,11 @@ namespace Squidex.Domain.Apps.Write.Contents A.CallTo(() => eventStore.GetEventsAsync(streamName)) .Returns(events); - A.CallTo(() => formatter.Parse(eventData1)) + A.CallTo(() => formatter.Parse(eventData1, true)) .Returns(new Envelope(event1)); - A.CallTo(() => formatter.Parse(eventData2)) + A.CallTo(() => formatter.Parse(eventData2, true)) .Returns(new Envelope(event2)); - A.CallTo(() => formatter.Parse(eventData3)) + A.CallTo(() => formatter.Parse(eventData3, true)) .Returns(new Envelope(event3)); var data = await sut.LoadAsync(appId, id, 1); diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaEventTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaEventTests.cs new file mode 100644 index 000000000..ac768d5c1 --- /dev/null +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaEventTests.cs @@ -0,0 +1,36 @@ +// ========================================================================== +// SchemaEventTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Domain.Apps.Events.Schemas.Old; +using Squidex.Domain.Apps.Write.TestHelpers; +using Squidex.Infrastructure.CQRS.Events; +using Xunit; + +#pragma warning disable CS0612 // Type or member is obsolete + +namespace Squidex.Domain.Apps.Write.Schemas +{ + public class SchemaEventTests + { + [Fact] + public void Should_migrate_webhook_added_event_to_noop() + { + var source = new WebhookAdded(); + + source.Migrate().ShouldBeSameEventType(new NoopEvent()); + } + + [Fact] + public void Should_migrate_webhook_deleted_event_to_noop() + { + var source = new WebhookDeleted(); + + source.Migrate().ShouldBeSameEventType(new NoopEvent()); + } + } +} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/TestHelpers/AssertHelper.cs b/tests/Squidex.Domain.Apps.Write.Tests/TestHelpers/AssertHelper.cs index 3a2e3c19d..8387e7a10 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/TestHelpers/AssertHelper.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/TestHelpers/AssertHelper.cs @@ -23,9 +23,23 @@ namespace Squidex.Domain.Apps.Write.TestHelpers for (var i = 0; i < source.Length; i++) { - source[i].Should().BeOfType(others[i].GetType()); - ((object)source[i]).ShouldBeEquivalentTo(others[i], o => o.IncludingAllDeclaredProperties()); + var lhs = source[i]; + var rhs = others[i]; + + lhs.ShouldBeSameEvent(rhs); } } + + public static void ShouldBeSameEvent(this IEvent lhs, IEvent rhs) + { + lhs.Should().BeOfType(rhs.GetType()); + + ((object)lhs).ShouldBeEquivalentTo(rhs, o => o.IncludingAllDeclaredProperties()); + } + + public static void ShouldBeSameEventType(this IEvent lhs, IEvent rhs) + { + lhs.Should().BeOfType(rhs.GetType()); + } } } diff --git a/tests/Squidex.Infrastructure.Tests/CQRS/Commands/DefaultDomainObjectRepositoryTests.cs b/tests/Squidex.Infrastructure.Tests/CQRS/Commands/DefaultDomainObjectRepositoryTests.cs index 44608bc6a..c5b7d5968 100644 --- a/tests/Squidex.Infrastructure.Tests/CQRS/Commands/DefaultDomainObjectRepositoryTests.cs +++ b/tests/Squidex.Infrastructure.Tests/CQRS/Commands/DefaultDomainObjectRepositoryTests.cs @@ -96,9 +96,9 @@ namespace Squidex.Infrastructure.CQRS.Commands A.CallTo(() => eventStore.GetEventsAsync(streamName)) .Returns(events); - A.CallTo(() => formatter.Parse(eventData1)) + A.CallTo(() => formatter.Parse(eventData1, true)) .Returns(new Envelope(event1)); - A.CallTo(() => formatter.Parse(eventData2)) + A.CallTo(() => formatter.Parse(eventData2, true)) .Returns(new Envelope(event2)); await sut.LoadAsync(domainObject); @@ -124,9 +124,9 @@ namespace Squidex.Infrastructure.CQRS.Commands A.CallTo(() => eventStore.GetEventsAsync(streamName)) .Returns(events); - A.CallTo(() => formatter.Parse(eventData1)) + A.CallTo(() => formatter.Parse(eventData1, true)) .Returns(new Envelope(event1)); - A.CallTo(() => formatter.Parse(eventData2)) + A.CallTo(() => formatter.Parse(eventData2, true)) .Returns(new Envelope(event2)); await Assert.ThrowsAsync(() => sut.LoadAsync(domainObject, 200)); @@ -143,9 +143,9 @@ namespace Squidex.Infrastructure.CQRS.Commands var eventData1 = new EventData(); var eventData2 = new EventData(); - A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event1), commitId)) + A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event1), commitId, true)) .Returns(eventData1); - A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event2), commitId)) + A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event2), commitId, true)) .Returns(eventData2); A.CallTo(() => eventStore.AppendEventsAsync(commitId, streamName, 123, A>.That.Matches(e => e.Count == 2))) @@ -170,9 +170,9 @@ namespace Squidex.Infrastructure.CQRS.Commands var eventData1 = new EventData(); var eventData2 = new EventData(); - A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event1), commitId)) + A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event1), commitId, true)) .Returns(eventData1); - A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event2), commitId)) + A.CallTo(() => formatter.ToEventData(A>.That.Matches(e => e.Payload == event2), commitId, true)) .Returns(eventData2); A.CallTo(() => eventStore.AppendEventsAsync(commitId, streamName, 123, A>.That.Matches(e => e.Count == 2))) diff --git a/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs index 771c24ab3..7e07d5dd4 100644 --- a/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs @@ -22,20 +22,35 @@ namespace Squidex.Infrastructure.CQRS.Events public string MyProperty { get; set; } } + public sealed class MyOldEvent : IEvent, IMigratedEvent + { + public string MyProperty { get; set; } + + public IEvent Migrate() + { + return new MyEvent { MyProperty = MyProperty }; + } + } + private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); + private readonly EventDataFormatter sut; public EventDataFormatterTests() { serializerSettings.Converters.Add(new PropertiesBagConverter()); typeNameRegistry.Map(typeof(MyEvent), "Event"); + typeNameRegistry.Map(typeof(MyOldEvent), "OldEvent"); + + sut = new EventDataFormatter(typeNameRegistry, serializerSettings); } [Fact] public void Should_serialize_and_deserialize_envelope() { var commitId = Guid.NewGuid(); + var inputEvent = new Envelope(new MyEvent { MyProperty = "My-Property" }); inputEvent.SetAggregateId(Guid.NewGuid()); @@ -45,18 +60,44 @@ namespace Squidex.Infrastructure.CQRS.Events inputEvent.SetEventStreamNumber(1); inputEvent.SetTimestamp(SystemClock.Instance.GetCurrentInstant()); - var sut = new EventDataFormatter(typeNameRegistry, serializerSettings); - var eventData = sut.ToEventData(inputEvent.To(), commitId); var outputEvent = sut.Parse(eventData).To(); - CompareHeaders(outputEvent.Headers, inputEvent.Headers); + AssertHeaders(inputEvent.Headers, outputEvent.Headers); + AssertPayload(inputEvent, outputEvent); + } + + [Fact] + public void Should_migrate_event_serializing() + { + var inputEvent = new Envelope(new MyOldEvent { MyProperty = "My-Property" }); + + var eventData = sut.ToEventData(inputEvent.To(), Guid.NewGuid()); + + var outputEvent = sut.Parse(eventData).To(); + + Assert.Equal(inputEvent.Payload.MyProperty, outputEvent.Payload.MyProperty); + } + + [Fact] + public void Should_migrate_event_deserializing() + { + var inputEvent = new Envelope(new MyOldEvent { MyProperty = "My-Property" }); + var eventData = sut.ToEventData(inputEvent.To(), Guid.NewGuid(), false); + + var outputEvent = sut.Parse(eventData).To(); + + Assert.Equal(inputEvent.Payload.MyProperty, outputEvent.Payload.MyProperty); + } + + private static void AssertPayload(Envelope inputEvent, Envelope outputEvent) + { Assert.Equal(inputEvent.Payload.MyProperty, outputEvent.Payload.MyProperty); } - private static void CompareHeaders(PropertiesBag lhs, PropertiesBag rhs) + private static void AssertHeaders(PropertiesBag lhs, PropertiesBag rhs) { foreach (var key in lhs.PropertyNames.Concat(rhs.PropertyNames).Distinct()) { diff --git a/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs index 2fbc3f40f..aa12aeccf 100644 --- a/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs +++ b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs @@ -127,9 +127,9 @@ namespace Squidex.Infrastructure.CQRS.Events A.CallTo(() => eventConsumer.Name).Returns(consumerName); A.CallTo(() => eventConsumerInfoRepository.FindAsync(consumerName)).Returns(consumerInfo); - A.CallTo(() => formatter.Parse(eventData1)).Returns(envelope1); - A.CallTo(() => formatter.Parse(eventData2)).Returns(envelope2); - A.CallTo(() => formatter.Parse(eventData3)).Returns(envelope3); + A.CallTo(() => formatter.Parse(eventData1, true)).Returns(envelope1); + A.CallTo(() => formatter.Parse(eventData2, true)).Returns(envelope2); + A.CallTo(() => formatter.Parse(eventData3, true)).Returns(envelope3); sut = new EventReceiver(formatter, eventStore, eventConsumerInfoRepository, log); } @@ -183,7 +183,7 @@ namespace Squidex.Infrastructure.CQRS.Events { consumerInfo.Position = "2"; - A.CallTo(() => formatter.Parse(eventData2)).Throws(new InvalidOperationException()); + A.CallTo(() => formatter.Parse(eventData2, true)).Throws(new InvalidOperationException()); sut.Subscribe(eventConsumer); sut.Refresh();