Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rasmus committed Jun 28, 2017
2 parents 4b87c01 + d51e0d5 commit fed1076
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 8 deletions.
12 changes: 11 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
### New in 0.46 (not released yet)
### New in 0.47 (not released yet)

* New: To be more explicit, `IEventFlowOpions.AddSynchronousSubscriber<,,,>` and
`IEventFlowOpions.AddAsynchronousSubscriber<,,,>` generic methods
* Fix: `IEventFlowOpions.AddSubscriber`, `IEventFlowOpions.AddSubscribers` and
`IEventFlowOpions.AddDefaults` now correctly registers implementations of
`ISubscribeAsynchronousTo<,,>`
* Obsolete: `IEventFlowOpions.AddSubscriber` is marked obsolete in favor of its
explicite counterparts

### New in 0.46.2886 (released 2017-05-29)

* Fix: EventFlow now uses a Autofac lifetime scope for validating service
registrations when `IEventFlowOpions.CreateResolver(true)` is invoked.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,45 @@ public void AbstractQueryHandlerIsNotRegistered()
// Assert
act.ShouldNotThrow<ArgumentException>();
}

[Test]
public async Task AggregateFactoryWorks()
{
// Arrange
Sut.Register<IAggregateFactory, AggregateFactory>();
var resolver = Sut.CreateResolver(false);
var aggregateFactory = resolver.Resolve<IAggregateFactory>();
var customId = new CustomId(A<string>());

// Act
var customAggregate = await aggregateFactory.CreateNewAggregateAsync<CustomAggregate, CustomId>(customId).ConfigureAwait(false);

// Assert
customAggregate.Id.Value.Should().Be(customId.Value);
}

public abstract class AbstractTestQueryHandler : IQueryHandler<ThingyGetQuery, Thingy>
{
public abstract Task<Thingy> ExecuteQueryAsync(ThingyGetQuery query,
CancellationToken cancellationToken);
}

public class CustomId : IIdentity
{
public CustomId(string value)
{
Value = value;
}

public string Value { get; }
}

public class CustomAggregate : AggregateRoot<CustomAggregate, CustomId>
{
public CustomAggregate(CustomId id) : base(id)
{
}
}
}

public abstract class AbstractTestAggregate : AggregateRoot<ThingyAggregate, ThingyId>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2016 Rasmus Mikkelsen
// Copyright (c) 2015-2016 eBay Software Foundation
// https://github.com/rasmus/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System.Threading;
using System.Threading.Tasks;
using EventFlow.Aggregates;
using EventFlow.Core;
using EventFlow.TestHelpers;
using FluentAssertions;
using NUnit.Framework;

namespace EventFlow.Tests.Exploration
{
public class CustomAggregateIdExplorationTest : Test
{
[Test]
public async Task AggregatesCanHaveCustomImplementedIdentity()
{
using (var resolver = EventFlowOptions.New
.CreateResolver(false))
{
// Arrange
var customId = new CustomId(A<string>());
var aggregateStore = resolver.Resolve<IAggregateStore>();

// Act
var customAggregate = await aggregateStore.LoadAsync<CustomAggregate, CustomId>(customId, CancellationToken.None).ConfigureAwait(false);

// Assert
customAggregate.Id.Value.Should().Be(customId.Value);
}
}

public class CustomId : IIdentity
{
public CustomId(string value)
{
Value = value;
}

public string Value { get; }
}

public class CustomAggregate : AggregateRoot<CustomAggregate, CustomId>
{
public CustomAggregate(CustomId id) : base(id)
{
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2017 Rasmus Mikkelsen
// Copyright (c) 2015-2017 eBay Software Foundation
// https://github.com/rasmus/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using EventFlow.Aggregates;
using EventFlow.Core;
using EventFlow.Extensions;
using EventFlow.Subscribers;
using EventFlow.TestHelpers;
using EventFlow.TestHelpers.Aggregates;
using EventFlow.TestHelpers.Aggregates.Commands;
using EventFlow.TestHelpers.Aggregates.Events;
using EventFlow.TestHelpers.Aggregates.ValueObjects;
using FluentAssertions;
using NUnit.Framework;

namespace EventFlow.Tests.Exploration
{
public class RegisterSubscribersExplorationTests : Test
{
[TestCaseSource(nameof(TestCases))]
public async Task TestRegisterAsynchronousSubscribersAsync(Func<IEventFlowOptions, IEventFlowOptions> register)
{
// Arrange
var wasHandled = false;
TestSubscriber.OnHandleAction = () => wasHandled = true;
using (new DisposableAction(() => TestSubscriber.OnHandleAction = null))
using (var resolver = register(EventFlowOptions.New)
.AddCommands(typeof(ThingyPingCommand))
.AddCommandHandlers(typeof(ThingyPingCommandHandler))
.AddEvents(typeof(ThingyPingEvent))
.Configure(c => c.IsAsynchronousSubscribersEnabled = true)
.CreateResolver(false))
{
var commandBus = resolver.Resolve<ICommandBus>();

// Act
await commandBus.PublishAsync(
new ThingyPingCommand(A<ThingyId>(), A<PingId>()),
CancellationToken.None)
.ConfigureAwait(false);
}

// Assert
wasHandled.Should().BeTrue();
}

public static IEnumerable<Func<IEventFlowOptions, IEventFlowOptions>> TestCases()
{
yield return o =>
{
Console.WriteLine("Using generic");
return o.AddAsynchronousSubscriber<ThingyAggregate, ThingyId, ThingyPingEvent, TestSubscriber>() ;
};
yield return o =>
{
Console.WriteLine("Using add specific type");
return o.AddSubscribers(typeof(TestSubscriber));
};
yield return o =>
{
Console.WriteLine("Using assembly scanner");
return o.AddSubscribers(typeof(RegisterSubscribersExplorationTests).Assembly, t => t == typeof(TestSubscriber));
};
}
}

public class TestSubscriber : ISubscribeAsynchronousTo<ThingyAggregate, ThingyId, ThingyPingEvent>
{
public static Action OnHandleAction { get; set; }

public Task HandleAsync(IDomainEvent<ThingyAggregate, ThingyId, ThingyPingEvent> domainEvent, CancellationToken cancellationToken)
{
OnHandleAction?.Invoke();
return Task.FromResult(0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace EventFlow.Extensions
{
public static class EventFlowOptionsSubscriberExtensions
{
[Obsolete("Please use the more explicite method 'AddSynchronousSubscriber<,,,>' instead")]
public static IEventFlowOptions AddSubscriber<TAggregate, TIdentity, TEvent, TSubscriber>(
this IEventFlowOptions eventFlowOptions)
where TAggregate : IAggregateRoot<TIdentity>
Expand All @@ -44,6 +45,28 @@ public static IEventFlowOptions AddSubscriber<TAggregate, TIdentity, TEvent, TSu
.RegisterServices(sr => sr.Register<ISubscribeSynchronousTo<TAggregate, TIdentity, TEvent>, TSubscriber>());
}

public static IEventFlowOptions AddSynchronousSubscriber<TAggregate, TIdentity, TEvent, TSubscriber>(
this IEventFlowOptions eventFlowOptions)
where TAggregate : IAggregateRoot<TIdentity>
where TIdentity : IIdentity
where TEvent : IAggregateEvent<TAggregate, TIdentity>
where TSubscriber : class, ISubscribeSynchronousTo<TAggregate, TIdentity, TEvent>
{
return eventFlowOptions
.RegisterServices(sr => sr.Register<ISubscribeSynchronousTo<TAggregate, TIdentity, TEvent>, TSubscriber>());
}

public static IEventFlowOptions AddAsynchronousSubscriber<TAggregate, TIdentity, TEvent, TSubscriber>(
this IEventFlowOptions eventFlowOptions)
where TAggregate : IAggregateRoot<TIdentity>
where TIdentity : IIdentity
where TEvent : IAggregateEvent<TAggregate, TIdentity>
where TSubscriber : class, ISubscribeAsynchronousTo<TAggregate, TIdentity, TEvent>
{
return eventFlowOptions
.RegisterServices(sr => sr.Register<ISubscribeAsynchronousTo<TAggregate, TIdentity, TEvent>, TSubscriber>());
}

public static IEventFlowOptions AddSubscribers(
this IEventFlowOptions eventFlowOptions,
params Type[] subscribeSynchronousToTypes)
Expand All @@ -56,15 +79,19 @@ public static IEventFlowOptions AddSubscribers(
Assembly fromAssembly,
Predicate<Type> predicate = null)
{
var iSubscribeSynchronousToType = typeof(ISubscribeSynchronousTo<,,>);
var iSubscribeAsynchronousToType = typeof(ISubscribeAsynchronousTo<,,>);
var iSubscribeSynchronousToAllType = typeof(ISubscribeSynchronousToAll);

predicate = predicate ?? (t => true);
var subscribeSynchronousToTypes = fromAssembly
.GetTypes()
.Where(t => t
.GetTypeInfo()
.GetInterfaces()
.Any(i =>
(i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(ISubscribeSynchronousTo<,,>)) ||
i == typeof(ISubscribeSynchronousToAll)))
i.GetTypeInfo().IsGenericType && (i.GetGenericTypeDefinition() == iSubscribeSynchronousToType || i.GetGenericTypeDefinition() == iSubscribeAsynchronousToType) ||
i == iSubscribeSynchronousToAllType))
.Where(t => predicate(t));
return eventFlowOptions.AddSubscribers(subscribeSynchronousToTypes);
}
Expand All @@ -73,6 +100,10 @@ public static IEventFlowOptions AddSubscribers(
this IEventFlowOptions eventFlowOptions,
IEnumerable<Type> subscribeSynchronousToTypes)
{
var iSubscribeSynchronousToType = typeof(ISubscribeSynchronousTo<,,>);
var iSubscribeAsynchronousToType = typeof(ISubscribeAsynchronousTo<,,>);
var iSubscribeSynchronousToAllType = typeof(ISubscribeSynchronousToAll);

foreach (var subscribeSynchronousToType in subscribeSynchronousToTypes)
{
var t = subscribeSynchronousToType;
Expand All @@ -81,12 +112,12 @@ public static IEventFlowOptions AddSubscribers(
.GetTypeInfo()
.GetInterfaces()
.Where(i =>
i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(ISubscribeSynchronousTo<,,>) ||
i == typeof(ISubscribeSynchronousToAll))
i.GetTypeInfo().IsGenericType && (i.GetGenericTypeDefinition() == iSubscribeSynchronousToType || i.GetGenericTypeDefinition() == iSubscribeAsynchronousToType) ||
i == iSubscribeSynchronousToAllType)
.ToList();
if (!subscribeTos.Any())
{
throw new ArgumentException($"Type '{t.PrettyPrint()}' is not an '{typeof(ISubscribeSynchronousTo<,,>).PrettyPrint()}'");
throw new ArgumentException($"Type '{t.PrettyPrint()}' is not an '{iSubscribeSynchronousToType.PrettyPrint()}', '{iSubscribeAsynchronousToType.PrettyPrint()}' or '{iSubscribeSynchronousToAllType.PrettyPrint()}'");
}

eventFlowOptions.RegisterServices(sr =>
Expand Down
8 changes: 7 additions & 1 deletion Source/EventFlow/Subscribers/DispatchToEventSubscribers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ private async Task DispatchToSubscribersAsync(
subscriberType,
cancellationToken)
.ConfigureAwait(false);
var subscribers = _resolver.ResolveAll(subscriberInfomation.SubscriberType);
var subscribers = _resolver.ResolveAll(subscriberInfomation.SubscriberType).ToList();

if (!subscribers.Any())
{
_log.Debug(() => $"Didn't find any subscribers to '{domainEvent.EventType.PrettyPrint()}'");
return;
}

foreach (var subscriber in subscribers)
{
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
init:
- git config --global core.autocrlf input

version: 0.46.{build}
version: 0.47.{build}

install:
- cmd: pip install -U Sphinx
Expand Down

0 comments on commit fed1076

Please sign in to comment.