mirror of https://github.com/Squidex/squidex.git
25 changed files with 361 additions and 150 deletions
@ -0,0 +1,53 @@ |
|||
// ==========================================================================
|
|||
// CompoundEventConsumer.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.CQRS.Events |
|||
{ |
|||
public sealed class CompoundEventConsumer : IEventConsumer |
|||
{ |
|||
private readonly IEventConsumer[] inners; |
|||
|
|||
public string Name { get; } |
|||
|
|||
public CompoundEventConsumer(IEventConsumer first, params IEventConsumer[] inners) |
|||
{ |
|||
Guard.NotNull(first, nameof(first)); |
|||
Guard.NotNull(inners, nameof(inners)); |
|||
|
|||
this.inners = new[] { first }.Union(inners).ToArray(); |
|||
|
|||
Name = first.GetType().Name; |
|||
} |
|||
|
|||
public CompoundEventConsumer(string name, params IEventConsumer[] inners) |
|||
{ |
|||
Guard.NotNull(inners, nameof(inners)); |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
|
|||
this.inners = inners; |
|||
|
|||
Name = name; |
|||
} |
|||
|
|||
public Task ClearAsync() |
|||
{ |
|||
return Task.WhenAll(inners.Select(i => i.ClearAsync())); |
|||
} |
|||
|
|||
public async Task On(Envelope<IEvent> @event) |
|||
{ |
|||
foreach (var inner in inners) |
|||
{ |
|||
await inner.On(@event); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// ==========================================================================
|
|||
// Envelope.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using NodaTime; |
|||
|
|||
namespace Squidex.Infrastructure.CQRS.Events |
|||
{ |
|||
public class Envelope<TPayload> where TPayload : class |
|||
{ |
|||
private readonly EnvelopeHeaders headers; |
|||
private readonly TPayload payload; |
|||
|
|||
public EnvelopeHeaders Headers |
|||
{ |
|||
get { return headers; } |
|||
} |
|||
|
|||
public TPayload Payload |
|||
{ |
|||
get { return payload; } |
|||
} |
|||
|
|||
public Envelope(TPayload payload) |
|||
: this(payload, new EnvelopeHeaders()) |
|||
{ |
|||
} |
|||
|
|||
public Envelope(TPayload payload, PropertiesBag bag) |
|||
: this(payload, new EnvelopeHeaders(bag)) |
|||
{ |
|||
} |
|||
|
|||
public Envelope(TPayload payload, EnvelopeHeaders headers) |
|||
{ |
|||
Guard.NotNull(payload, nameof(payload)); |
|||
Guard.NotNull(headers, nameof(headers)); |
|||
|
|||
this.payload = payload; |
|||
this.headers = headers; |
|||
} |
|||
|
|||
public static Envelope<TPayload> Create(TPayload payload) |
|||
{ |
|||
var eventId = Guid.NewGuid(); |
|||
|
|||
var envelope = |
|||
new Envelope<TPayload>(payload) |
|||
.SetEventId(eventId) |
|||
.SetTimestamp(SystemClock.Instance.GetCurrentInstant()); |
|||
|
|||
return envelope; |
|||
} |
|||
|
|||
public Envelope<TOther> To<TOther>() where TOther : class |
|||
{ |
|||
return new Envelope<TOther>(payload as TOther, headers.Clone()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// ==========================================================================
|
|||
// CompoundEventConsumerTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Moq; |
|||
using Squidex.Infrastructure.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.CQRS.Events |
|||
{ |
|||
public class CompoundEventConsumerTests |
|||
{ |
|||
private readonly Mock<IEventConsumer> consumer1 = new Mock<IEventConsumer>(); |
|||
private readonly Mock<IEventConsumer> consumer2 = new Mock<IEventConsumer>(); |
|||
|
|||
private sealed class MyEvent : IEvent |
|||
{ |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_given_name() |
|||
{ |
|||
var sut = new CompoundEventConsumer("consumer-name"); |
|||
|
|||
Assert.Equal("consumer-name", sut.Name); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_first_inner_name() |
|||
{ |
|||
var sut = new CompoundEventConsumer(consumer1.Object, consumer2.Object); |
|||
|
|||
Assert.Equal(consumer1.Object.GetType().Name, sut.Name); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_clear_all_consumers() |
|||
{ |
|||
consumer1.Setup(x => x.ClearAsync()).Returns(TaskHelper.Done).Verifiable(); |
|||
consumer2.Setup(x => x.ClearAsync()).Returns(TaskHelper.Done).Verifiable(); |
|||
|
|||
var sut = new CompoundEventConsumer("consumer-name", consumer1.Object, consumer2.Object); |
|||
|
|||
await sut.ClearAsync(); |
|||
|
|||
consumer1.VerifyAll(); |
|||
consumer2.VerifyAll(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_invoke_all_consumers() |
|||
{ |
|||
var @event = Envelope.Create(new MyEvent()); |
|||
|
|||
consumer1.Setup(x => x.On(@event)).Returns(TaskHelper.Done).Verifiable(); |
|||
consumer2.Setup(x => x.On(@event)).Returns(TaskHelper.Done).Verifiable(); |
|||
|
|||
var sut = new CompoundEventConsumer("consumer-name", consumer1.Object, consumer2.Object); |
|||
|
|||
await sut.On(@event); |
|||
|
|||
consumer1.VerifyAll(); |
|||
consumer2.VerifyAll(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue