Browse Source

Event migration.

pull/111/head
Sebastian Stehle 8 years ago
parent
commit
10644150e8
  1. 13
      src/Squidex.Domain.Apps.Core/Schemas/FieldRegistry.cs
  2. 10
      src/Squidex.Domain.Apps.Events/Contents/Old/ContentArchived.cs
  3. 10
      src/Squidex.Domain.Apps.Events/Contents/Old/ContentPublished.cs
  4. 10
      src/Squidex.Domain.Apps.Events/Contents/Old/ContentRestored.cs
  5. 10
      src/Squidex.Domain.Apps.Events/Contents/Old/ContentUnpublished.cs
  6. 7
      src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs
  7. 7
      src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs
  8. 47
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs
  9. 11
      src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs
  10. 10
      src/Squidex.Domain.Apps.Write/Schemas/SchemaEvents.cs
  11. 22
      src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs
  12. 15
      src/Squidex.Infrastructure/CQRS/Events/IMigratedEvent.cs
  13. 14
      src/Squidex.Infrastructure/CQRS/Events/NoopEvent.cs
  14. 70
      tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs
  15. 12
      tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs
  16. 36
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaEventTests.cs
  17. 18
      tests/Squidex.Domain.Apps.Write.Tests/TestHelpers/AssertHelper.cs
  18. 16
      tests/Squidex.Infrastructure.Tests/CQRS/Commands/DefaultDomainObjectRepositoryTests.cs
  19. 49
      tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs
  20. 8
      tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs

13
src/Squidex.Domain.Apps.Core/Schemas/FieldRegistry.cs

@ -59,10 +59,6 @@ namespace Squidex.Domain.Apps.Core.Schemas
(id, name, partitioning, properties) => (id, name, partitioning, properties) =>
new StringField(id, name, partitioning, (StringFieldProperties)properties)); new StringField(id, name, partitioning, (StringFieldProperties)properties));
Add<DateTimeFieldProperties>(
(id, name, partitioning, properties) =>
new DateTimeField(id, name, partitioning, (DateTimeFieldProperties)properties));
Add<JsonFieldProperties>( Add<JsonFieldProperties>(
(id, name, partitioning, properties) => (id, name, partitioning, properties) =>
new JsonField(id, name, partitioning, (JsonFieldProperties)properties)); new JsonField(id, name, partitioning, (JsonFieldProperties)properties));
@ -71,15 +67,20 @@ namespace Squidex.Domain.Apps.Core.Schemas
(id, name, partitioning, properties) => (id, name, partitioning, properties) =>
new AssetsField(id, name, partitioning, (AssetsFieldProperties)properties)); new AssetsField(id, name, partitioning, (AssetsFieldProperties)properties));
Add<GeolocationFieldProperties>(
(id, name, partitioning, properties) =>
new GeolocationField(id, name, partitioning, (GeolocationFieldProperties)properties));
Add<ReferencesFieldProperties>( Add<ReferencesFieldProperties>(
(id, name, partitioning, properties) => (id, name, partitioning, properties) =>
new ReferencesField(id, name, partitioning, (ReferencesFieldProperties)properties)); new ReferencesField(id, name, partitioning, (ReferencesFieldProperties)properties));
Add<GeolocationFieldProperties>( Add<DateTimeFieldProperties>(
(id, name, partitioning, properties) => (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(ReferencesFieldProperties), "DateTime");
typeNameRegistry.MapObsolete(typeof(DateTimeFieldProperties), "References"); typeNameRegistry.MapObsolete(typeof(DateTimeFieldProperties), "References");
} }

10
src/Squidex.Domain.Apps.Events/Contents/Old/ContentArchived.cs

@ -7,13 +7,19 @@
// ========================================================================== // ==========================================================================
using System; using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.CQRS.Events; 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))] [EventType(nameof(ContentArchived))]
[Obsolete] [Obsolete]
public sealed class ContentArchived : ContentEvent public sealed class ContentArchived : ContentEvent, IMigratedEvent
{ {
public IEvent Migrate()
{
return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Archived });
}
} }
} }

10
src/Squidex.Domain.Apps.Events/Contents/Old/ContentPublished.cs

@ -7,13 +7,19 @@
// ========================================================================== // ==========================================================================
using System; using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.CQRS.Events; 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))] [EventType(nameof(ContentPublished))]
[Obsolete] [Obsolete]
public sealed class ContentPublished : ContentEvent public sealed class ContentPublished : ContentEvent, IMigratedEvent
{ {
public IEvent Migrate()
{
return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Published });
}
} }
} }

10
src/Squidex.Domain.Apps.Events/Contents/Old/ContentRestored.cs

@ -7,13 +7,19 @@
// ========================================================================== // ==========================================================================
using System; using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.CQRS.Events; 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))] [EventType(nameof(ContentRestored))]
[Obsolete] [Obsolete]
public sealed class ContentRestored : ContentEvent public sealed class ContentRestored : ContentEvent, IMigratedEvent
{ {
public IEvent Migrate()
{
return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Draft });
}
} }
} }

10
src/Squidex.Domain.Apps.Events/Contents/Old/ContentUnpublished.cs

@ -7,13 +7,19 @@
// ========================================================================== // ==========================================================================
using System; using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.CQRS.Events; 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))] [EventType(nameof(ContentUnpublished))]
[Obsolete] [Obsolete]
public sealed class ContentUnpublished : ContentEvent public sealed class ContentUnpublished : ContentEvent, IMigratedEvent
{ {
public IEvent Migrate()
{
return SimpleMapper.Map(this, new ContentStatusChanged { Status = Status.Draft });
}
} }
} }

7
src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs

@ -13,12 +13,17 @@ namespace Squidex.Domain.Apps.Events.Schemas.Old
{ {
[EventType(nameof(WebhookAdded))] [EventType(nameof(WebhookAdded))]
[Obsolete] [Obsolete]
public sealed class WebhookAdded : SchemaEvent public sealed class WebhookAdded : SchemaEvent, IMigratedEvent
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Uri Url { get; set; } public Uri Url { get; set; }
public string SharedSecret { get; set; } public string SharedSecret { get; set; }
public IEvent Migrate()
{
return new NoopEvent();
}
} }
} }

7
src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs

@ -13,8 +13,13 @@ namespace Squidex.Domain.Apps.Events.Schemas.Old
{ {
[EventType(nameof(WebhookDeleted))] [EventType(nameof(WebhookDeleted))]
[Obsolete] [Obsolete]
public sealed class WebhookDeleted : SchemaEvent public sealed class WebhookDeleted : SchemaEvent, IMigratedEvent
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public IEvent Migrate()
{
return new NoopEvent();
}
} }
} }

47
src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs

@ -9,7 +9,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Contents; using Squidex.Domain.Apps.Events.Contents;
@ -18,8 +17,6 @@ using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Squidex.Domain.Apps.Read.MongoDb.Contents namespace Squidex.Domain.Apps.Read.MongoDb.Contents
{ {
public partial class MongoContentRepository 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) protected Task On(ContentStatusChanged @event, EnvelopeHeaders headers)
{ {
return ForAppIdAsync(@event.AppId.Id, collection => 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) protected Task On(AssetDeleted @event, EnvelopeHeaders headers)
{ {
return ForAppIdAsync(@event.AppId.Id, collection => return ForAppIdAsync(@event.AppId.Id, collection =>

11
src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs

@ -12,8 +12,6 @@ using Squidex.Domain.Apps.Read.History;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Squidex.Domain.Apps.Read.Contents namespace Squidex.Domain.Apps.Read.Contents
{ {
public sealed class ContentHistoryEventsCreator : HistoryEventsCreatorBase public sealed class ContentHistoryEventsCreator : HistoryEventsCreatorBase
@ -30,15 +28,6 @@ namespace Squidex.Domain.Apps.Read.Contents
AddEventMessage<ContentDeleted>( AddEventMessage<ContentDeleted>(
"deleted content item."); "deleted content item.");
AddEventMessage<ContentRestored>(
"restored content item.");
AddEventMessage<ContentPublished>(
"published content item.");
AddEventMessage<ContentUnpublished>(
"unpublished content item.");
AddEventMessage<ContentStatusChanged>( AddEventMessage<ContentStatusChanged>(
"change status of content item to {[Status]}."); "change status of content item to {[Status]}.");
} }

10
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
{
}
}

22
src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs

@ -25,21 +25,33 @@ namespace Squidex.Infrastructure.CQRS.Events
this.serializerSettings = serializerSettings ?? new JsonSerializerSettings(); this.serializerSettings = serializerSettings ?? new JsonSerializerSettings();
} }
public virtual Envelope<IEvent> Parse(EventData eventData) public virtual Envelope<IEvent> Parse(EventData eventData, bool migrate = true)
{ {
var headers = ReadJson<PropertiesBag>(eventData.Metadata); var headers = ReadJson<PropertiesBag>(eventData.Metadata);
var eventType = typeNameRegistry.GetType(eventData.Type); var eventType = typeNameRegistry.GetType(eventData.Type);
var eventContent = ReadJson<IEvent>(eventData.Payload, eventType); var eventPayload = ReadJson<IEvent>(eventData.Payload, eventType);
var envelope = new Envelope<IEvent>(eventContent, headers); if (migrate && eventPayload is IMigratedEvent migratedEvent)
{
eventPayload = migratedEvent.Migrate();
}
var envelope = new Envelope<IEvent>(eventPayload, headers);
return envelope; return envelope;
} }
public virtual EventData ToEventData(Envelope<IEvent> envelope, Guid commitId) public virtual EventData ToEventData(Envelope<IEvent> 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); envelope.SetCommitId(commitId);

15
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();
}
}

14
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
{
}
}

70
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<Guid> appId = new NamedId<Guid>(Guid.NewGuid(), "my-app");
private readonly NamedId<Guid> schemaId = new NamedId<Guid>(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>(T contentEvent) where T : ContentEvent
{
contentEvent.Actor = actor;
contentEvent.AppId = appId;
contentEvent.SchemaId = schemaId;
contentEvent.ContentId = contentId;
return contentEvent;
}
}
}

12
tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs

@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Write.Contents
A.CallTo(() => eventStore.GetEventsAsync(streamName)) A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events); .Returns(events);
A.CallTo(() => formatter.Parse(eventData1)) A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1)); .Returns(new Envelope<IEvent>(event1));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, 0)); await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, 0));
@ -100,9 +100,9 @@ namespace Squidex.Domain.Apps.Write.Contents
A.CallTo(() => eventStore.GetEventsAsync(streamName)) A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events); .Returns(events);
A.CallTo(() => formatter.Parse(eventData1)) A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1)); .Returns(new Envelope<IEvent>(event1));
A.CallTo(() => formatter.Parse(eventData2)) A.CallTo(() => formatter.Parse(eventData2, true))
.Returns(new Envelope<IEvent>(event2)); .Returns(new Envelope<IEvent>(event2));
var data = await sut.LoadAsync(appId, id, 3); var data = await sut.LoadAsync(appId, id, 3);
@ -131,11 +131,11 @@ namespace Squidex.Domain.Apps.Write.Contents
A.CallTo(() => eventStore.GetEventsAsync(streamName)) A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events); .Returns(events);
A.CallTo(() => formatter.Parse(eventData1)) A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1)); .Returns(new Envelope<IEvent>(event1));
A.CallTo(() => formatter.Parse(eventData2)) A.CallTo(() => formatter.Parse(eventData2, true))
.Returns(new Envelope<IEvent>(event2)); .Returns(new Envelope<IEvent>(event2));
A.CallTo(() => formatter.Parse(eventData3)) A.CallTo(() => formatter.Parse(eventData3, true))
.Returns(new Envelope<IEvent>(event3)); .Returns(new Envelope<IEvent>(event3));
var data = await sut.LoadAsync(appId, id, 1); var data = await sut.LoadAsync(appId, id, 1);

36
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());
}
}
}

18
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++) for (var i = 0; i < source.Length; i++)
{ {
source[i].Should().BeOfType(others[i].GetType()); var lhs = source[i];
((object)source[i]).ShouldBeEquivalentTo(others[i], o => o.IncludingAllDeclaredProperties()); 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());
} }
} }
} }

16
tests/Squidex.Infrastructure.Tests/CQRS/Commands/DefaultDomainObjectRepositoryTests.cs

@ -96,9 +96,9 @@ namespace Squidex.Infrastructure.CQRS.Commands
A.CallTo(() => eventStore.GetEventsAsync(streamName)) A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events); .Returns(events);
A.CallTo(() => formatter.Parse(eventData1)) A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1)); .Returns(new Envelope<IEvent>(event1));
A.CallTo(() => formatter.Parse(eventData2)) A.CallTo(() => formatter.Parse(eventData2, true))
.Returns(new Envelope<IEvent>(event2)); .Returns(new Envelope<IEvent>(event2));
await sut.LoadAsync(domainObject); await sut.LoadAsync(domainObject);
@ -124,9 +124,9 @@ namespace Squidex.Infrastructure.CQRS.Commands
A.CallTo(() => eventStore.GetEventsAsync(streamName)) A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events); .Returns(events);
A.CallTo(() => formatter.Parse(eventData1)) A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1)); .Returns(new Envelope<IEvent>(event1));
A.CallTo(() => formatter.Parse(eventData2)) A.CallTo(() => formatter.Parse(eventData2, true))
.Returns(new Envelope<IEvent>(event2)); .Returns(new Envelope<IEvent>(event2));
await Assert.ThrowsAsync<DomainObjectVersionException>(() => sut.LoadAsync(domainObject, 200)); await Assert.ThrowsAsync<DomainObjectVersionException>(() => sut.LoadAsync(domainObject, 200));
@ -143,9 +143,9 @@ namespace Squidex.Infrastructure.CQRS.Commands
var eventData1 = new EventData(); var eventData1 = new EventData();
var eventData2 = new EventData(); var eventData2 = new EventData();
A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event1), commitId)) A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event1), commitId, true))
.Returns(eventData1); .Returns(eventData1);
A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event2), commitId)) A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event2), commitId, true))
.Returns(eventData2); .Returns(eventData2);
A.CallTo(() => eventStore.AppendEventsAsync(commitId, streamName, 123, A<ICollection<EventData>>.That.Matches(e => e.Count == 2))) A.CallTo(() => eventStore.AppendEventsAsync(commitId, streamName, 123, A<ICollection<EventData>>.That.Matches(e => e.Count == 2)))
@ -170,9 +170,9 @@ namespace Squidex.Infrastructure.CQRS.Commands
var eventData1 = new EventData(); var eventData1 = new EventData();
var eventData2 = new EventData(); var eventData2 = new EventData();
A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event1), commitId)) A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event1), commitId, true))
.Returns(eventData1); .Returns(eventData1);
A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event2), commitId)) A.CallTo(() => formatter.ToEventData(A<Envelope<IEvent>>.That.Matches(e => e.Payload == event2), commitId, true))
.Returns(eventData2); .Returns(eventData2);
A.CallTo(() => eventStore.AppendEventsAsync(commitId, streamName, 123, A<ICollection<EventData>>.That.Matches(e => e.Count == 2))) A.CallTo(() => eventStore.AppendEventsAsync(commitId, streamName, 123, A<ICollection<EventData>>.That.Matches(e => e.Count == 2)))

49
tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs

@ -22,20 +22,35 @@ namespace Squidex.Infrastructure.CQRS.Events
public string MyProperty { get; set; } 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 JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry();
private readonly EventDataFormatter sut;
public EventDataFormatterTests() public EventDataFormatterTests()
{ {
serializerSettings.Converters.Add(new PropertiesBagConverter()); serializerSettings.Converters.Add(new PropertiesBagConverter());
typeNameRegistry.Map(typeof(MyEvent), "Event"); typeNameRegistry.Map(typeof(MyEvent), "Event");
typeNameRegistry.Map(typeof(MyOldEvent), "OldEvent");
sut = new EventDataFormatter(typeNameRegistry, serializerSettings);
} }
[Fact] [Fact]
public void Should_serialize_and_deserialize_envelope() public void Should_serialize_and_deserialize_envelope()
{ {
var commitId = Guid.NewGuid(); var commitId = Guid.NewGuid();
var inputEvent = new Envelope<MyEvent>(new MyEvent { MyProperty = "My-Property" }); var inputEvent = new Envelope<MyEvent>(new MyEvent { MyProperty = "My-Property" });
inputEvent.SetAggregateId(Guid.NewGuid()); inputEvent.SetAggregateId(Guid.NewGuid());
@ -45,18 +60,44 @@ namespace Squidex.Infrastructure.CQRS.Events
inputEvent.SetEventStreamNumber(1); inputEvent.SetEventStreamNumber(1);
inputEvent.SetTimestamp(SystemClock.Instance.GetCurrentInstant()); inputEvent.SetTimestamp(SystemClock.Instance.GetCurrentInstant());
var sut = new EventDataFormatter(typeNameRegistry, serializerSettings);
var eventData = sut.ToEventData(inputEvent.To<IEvent>(), commitId); var eventData = sut.ToEventData(inputEvent.To<IEvent>(), commitId);
var outputEvent = sut.Parse(eventData).To<MyEvent>(); var outputEvent = sut.Parse(eventData).To<MyEvent>();
CompareHeaders(outputEvent.Headers, inputEvent.Headers); AssertHeaders(inputEvent.Headers, outputEvent.Headers);
AssertPayload(inputEvent, outputEvent);
}
[Fact]
public void Should_migrate_event_serializing()
{
var inputEvent = new Envelope<MyOldEvent>(new MyOldEvent { MyProperty = "My-Property" });
var eventData = sut.ToEventData(inputEvent.To<IEvent>(), Guid.NewGuid());
var outputEvent = sut.Parse(eventData).To<MyEvent>();
Assert.Equal(inputEvent.Payload.MyProperty, outputEvent.Payload.MyProperty);
}
[Fact]
public void Should_migrate_event_deserializing()
{
var inputEvent = new Envelope<MyOldEvent>(new MyOldEvent { MyProperty = "My-Property" });
var eventData = sut.ToEventData(inputEvent.To<IEvent>(), Guid.NewGuid(), false);
var outputEvent = sut.Parse(eventData).To<MyEvent>();
Assert.Equal(inputEvent.Payload.MyProperty, outputEvent.Payload.MyProperty);
}
private static void AssertPayload(Envelope<MyEvent> inputEvent, Envelope<MyEvent> outputEvent)
{
Assert.Equal(inputEvent.Payload.MyProperty, outputEvent.Payload.MyProperty); 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()) foreach (var key in lhs.PropertyNames.Concat(rhs.PropertyNames).Distinct())
{ {

8
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(() => eventConsumer.Name).Returns(consumerName);
A.CallTo(() => eventConsumerInfoRepository.FindAsync(consumerName)).Returns(consumerInfo); A.CallTo(() => eventConsumerInfoRepository.FindAsync(consumerName)).Returns(consumerInfo);
A.CallTo(() => formatter.Parse(eventData1)).Returns(envelope1); A.CallTo(() => formatter.Parse(eventData1, true)).Returns(envelope1);
A.CallTo(() => formatter.Parse(eventData2)).Returns(envelope2); A.CallTo(() => formatter.Parse(eventData2, true)).Returns(envelope2);
A.CallTo(() => formatter.Parse(eventData3)).Returns(envelope3); A.CallTo(() => formatter.Parse(eventData3, true)).Returns(envelope3);
sut = new EventReceiver(formatter, eventStore, eventConsumerInfoRepository, log); sut = new EventReceiver(formatter, eventStore, eventConsumerInfoRepository, log);
} }
@ -183,7 +183,7 @@ namespace Squidex.Infrastructure.CQRS.Events
{ {
consumerInfo.Position = "2"; consumerInfo.Position = "2";
A.CallTo(() => formatter.Parse(eventData2)).Throws(new InvalidOperationException()); A.CallTo(() => formatter.Parse(eventData2, true)).Throws(new InvalidOperationException());
sut.Subscribe(eventConsumer); sut.Subscribe(eventConsumer);
sut.Refresh(); sut.Refresh();

Loading…
Cancel
Save