Skip to content

Commit

Permalink
added event filter contravariance
Browse files Browse the repository at this point in the history
  • Loading branch information
jonassamuelssoncab committed Sep 20, 2020
1 parent 455dc67 commit 97e06ee
Show file tree
Hide file tree
Showing 21 changed files with 121 additions and 78 deletions.
6 changes: 2 additions & 4 deletions src/Handyman.Mediator/src/IEventFilter.cs
@@ -1,11 +1,9 @@
using System.Threading.Tasks;
using Handyman.Mediator.Pipeline;

namespace Handyman.Mediator
{
public interface IEventFilter<TEvent>
where TEvent : IEvent
public interface IEventFilter<in TEvent>
{
Task Execute(EventPipelineContext<TEvent> context, EventFilterExecutionDelegate next);
Task Execute(TEvent @event, IEventFilterContext context, EventFilterExecutionDelegate next);
}
}
11 changes: 11 additions & 0 deletions src/Handyman.Mediator/src/IEventFilterContext.cs
@@ -0,0 +1,11 @@
using System;
using System.Threading;

namespace Handyman.Mediator
{
public interface IEventFilterContext
{
CancellationToken CancellationToken { get; set; }
IServiceProvider ServiceProvider { get; set; }
}
}
Expand Up @@ -13,7 +13,8 @@ public EventFilterToggleFilterSelector(EventFilterToggleMetadata toggleMetadata)
_toggleMetadata = toggleMetadata;
}

public async Task SelectFilters<TEvent>(List<IEventFilter<TEvent>> filters, EventPipelineContext<TEvent> context) where TEvent : IEvent
public async Task SelectFilters<TEvent>(List<IEventFilter<TEvent>> filters, EventPipelineContext<TEvent> context)
where TEvent : IEvent
{
var toggle = context.ServiceProvider.GetRequiredService<IEventFilterToggle>();
var enabled = await toggle.IsEnabled(_toggleMetadata, context).ConfigureAwait();
Expand Down
Expand Up @@ -4,8 +4,7 @@ namespace Handyman.Mediator.Pipeline.EventFilterToggle
{
public interface IEventFilterToggle
{
Task<bool> IsEnabled<TEvent>(EventFilterToggleMetadata toggleMetadata,
EventPipelineContext<TEvent> pipelineContext)
Task<bool> IsEnabled<TEvent>(EventFilterToggleMetadata toggleMetadata, EventPipelineContext<TEvent> pipelineContext)
where TEvent : IEvent;
}
}
Expand Up @@ -13,7 +13,8 @@ public EventHandlerToggleHandlerSelector(EventHandlerToggleMetadata toggleMetada
_toggleMetadata = toggleMetadata;
}

public async Task SelectHandlers<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context) where TEvent : IEvent
public async Task SelectHandlers<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context)
where TEvent : IEvent
{
var toggle = context.ServiceProvider.GetRequiredService<IEventHandlerToggle>();
var enabled = await toggle.IsEnabled(_toggleMetadata, context).ConfigureAwait();
Expand Down
Expand Up @@ -4,8 +4,7 @@ namespace Handyman.Mediator.Pipeline.EventHandlerToggle
{
public interface IEventHandlerToggle
{
Task<bool> IsEnabled<TEvent>(EventHandlerToggleMetadata toggleMetadata,
EventPipelineContext<TEvent> pipelineContext)
Task<bool> IsEnabled<TEvent>(EventHandlerToggleMetadata toggleMetadata, EventPipelineContext<TEvent> pipelineContext)
where TEvent : IEvent;
}
}
16 changes: 1 addition & 15 deletions src/Handyman.Mediator/src/Pipeline/EventPipeline.cs
Expand Up @@ -8,20 +8,6 @@ namespace Handyman.Mediator.Pipeline
internal abstract class EventPipeline
{
internal abstract Task Execute(IEvent @event, IServiceProvider serviceProvider, CancellationToken cancellationToken);

private static Task Execute<TEvent>(List<IEventHandler<TEvent>> handlers, TEvent @event, CancellationToken cancellationToken) where TEvent : IEvent
{
cancellationToken.ThrowIfCancellationRequested();

var tasks = new List<Task>(handlers.Count);

foreach (var handler in handlers)
{
tasks.Add(handler.Handle(@event, cancellationToken));
}

return Task.WhenAll(tasks);
}
}

internal abstract class EventPipeline<TEvent> : EventPipeline
Expand Down Expand Up @@ -65,7 +51,7 @@ Task Execute()
if (index < filterCount)
{
context.CancellationToken.ThrowIfCancellationRequested();
return filters[index++].Execute(context, Execute);
return filters[index++].Execute(context.Event, context, Execute);
}

context.CancellationToken.ThrowIfCancellationRequested();
Expand Down
3 changes: 2 additions & 1 deletion src/Handyman.Mediator/src/Pipeline/EventPipelineContext.cs
Expand Up @@ -3,7 +3,8 @@

namespace Handyman.Mediator.Pipeline
{
public class EventPipelineContext<TEvent> : IPipelineContext
public class EventPipelineContext<TEvent> : IEventFilterContext, IPipelineContext
where TEvent : IEvent
{
public CancellationToken CancellationToken { get; set; }
public TEvent Event { get; set; }
Expand Down
3 changes: 2 additions & 1 deletion src/Handyman.Mediator/src/Pipeline/IEventFilterSelector.cs
Expand Up @@ -5,6 +5,7 @@ namespace Handyman.Mediator.Pipeline
{
public interface IEventFilterSelector
{
Task SelectFilters<TEvent>(List<IEventFilter<TEvent>> filters, EventPipelineContext<TEvent> context) where TEvent : IEvent;
Task SelectFilters<TEvent>(List<IEventFilter<TEvent>> filters, EventPipelineContext<TEvent> context)
where TEvent : IEvent;
}
}
Expand Up @@ -5,6 +5,7 @@ namespace Handyman.Mediator.Pipeline
{
public interface IEventHandlerExecutionStrategy
{
Task Execute<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context) where TEvent : IEvent;
Task Execute<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context)
where TEvent : IEvent;
}
}
3 changes: 2 additions & 1 deletion src/Handyman.Mediator/src/Pipeline/IEventHandlerSelector.cs
Expand Up @@ -5,6 +5,7 @@ namespace Handyman.Mediator.Pipeline
{
public interface IEventHandlerSelector
{
Task SelectHandlers<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context) where TEvent : IEvent;
Task SelectHandlers<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context)
where TEvent : IEvent;
}
}
20 changes: 10 additions & 10 deletions src/Handyman.Mediator/src/Pipeline/ToggleBase.cs
Expand Up @@ -9,32 +9,32 @@ namespace Handyman.Mediator.Pipeline
{
public abstract class ToggleBase : IEventFilterToggle, IEventHandlerToggle, IRequestFilterToggle, IRequestHandlerToggle, IRequestHandlerExperimentToggle
{
public virtual Task<bool> IsEnabled<TEvent>(EventFilterToggleMetadata toggleMetadata,
EventPipelineContext<TEvent> pipelineContext) where TEvent : IEvent
public virtual Task<bool> IsEnabled<TEvent>(EventFilterToggleMetadata toggleMetadata, EventPipelineContext<TEvent> pipelineContext)
where TEvent : IEvent
{
return IsEnabled((IToggleMetadata)toggleMetadata, pipelineContext);
}

public virtual Task<bool> IsEnabled<TEvent>(EventHandlerToggleMetadata toggleMetadata,
EventPipelineContext<TEvent> pipelineContext) where TEvent : IEvent
public virtual Task<bool> IsEnabled<TEvent>(EventHandlerToggleMetadata toggleMetadata, EventPipelineContext<TEvent> pipelineContext)
where TEvent : IEvent
{
return IsEnabled((IToggleMetadata)toggleMetadata, pipelineContext);
}

public virtual Task<bool> IsEnabled<TRequest, TResponse>(RequestFilterToggleMetadata toggleMetadata,
RequestPipelineContext<TRequest> pipelineContext) where TRequest : IRequest<TResponse>
public virtual Task<bool> IsEnabled<TRequest, TResponse>(RequestFilterToggleMetadata toggleMetadata, RequestPipelineContext<TRequest> pipelineContext)
where TRequest : IRequest<TResponse>
{
return IsEnabled(toggleMetadata, pipelineContext);
}

public virtual Task<bool> IsEnabled<TRequest, TResponse>(RequestHandlerToggleMetadata toggleMetadata,
RequestPipelineContext<TRequest> pipelineContext) where TRequest : IRequest<TResponse>
public virtual Task<bool> IsEnabled<TRequest, TResponse>(RequestHandlerToggleMetadata toggleMetadata, RequestPipelineContext<TRequest> pipelineContext)
where TRequest : IRequest<TResponse>
{
return IsEnabled(toggleMetadata, pipelineContext);
}

public virtual Task<bool> IsEnabled<TRequest, TResponse>(RequestHandlerExperimentMetadata experimentMetadata,
RequestPipelineContext<TRequest> pipelineContext) where TRequest : IRequest<TResponse>
public virtual Task<bool> IsEnabled<TRequest, TResponse>(RequestHandlerExperimentMetadata experimentMetadata, RequestPipelineContext<TRequest> pipelineContext)
where TRequest : IRequest<TResponse>
{
return IsEnabled(experimentMetadata, pipelineContext);
}
Expand Down
Expand Up @@ -8,7 +8,8 @@ public sealed class WhenAllEventHandlerExecutionStrategy : IEventHandlerExecutio
{
public static readonly IEventHandlerExecutionStrategy Instance = new WhenAllEventHandlerExecutionStrategy();

public Task Execute<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context) where TEvent : IEvent
public Task Execute<TEvent>(List<IEventHandler<TEvent>> handlers, EventPipelineContext<TEvent> context)
where TEvent : IEvent
{
return Task.WhenAll(handlers.Select(x => x.Handle(context.Event, context.CancellationToken)));
}
Expand Down
11 changes: 5 additions & 6 deletions src/Handyman.Mediator/tests/EventFilterOrderingTests.cs
@@ -1,8 +1,7 @@
using System.Threading;
using System.Threading.Tasks;
using Handyman.Mediator.Pipeline;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Handyman.Mediator.Tests
Expand Down Expand Up @@ -37,9 +36,9 @@ private class Filter : IOrderedFilter, IEventFilter<Event>
public int Order { get; set; }
public string Text { get; set; }

public Task Execute(EventPipelineContext<Event> context, EventFilterExecutionDelegate next)
public Task Execute(Event @event, IEventFilterContext context, EventFilterExecutionDelegate next)
{
context.Event.Text += Text;
@event.Text += Text;
return next();
}
}
Expand Down
38 changes: 38 additions & 0 deletions src/Handyman.Mediator/tests/EventFilterTests.cs
@@ -0,0 +1,38 @@
using Lamar;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using System.Threading.Tasks;
using Xunit;

namespace Handyman.Mediator.Tests
{
public class EventFilterTests
{
[Fact]
public async Task ShouldExecuteFilters()
{
// using Lamar for dependency injection as it has support for constrained open generics

var container = new Container(services => services.AddTransient<IEventFilter<IMarker>, Filter>());

await new Mediator(container).Publish(new Event());

Filter.Executed.ShouldBeTrue();
}

private class Event : IEvent, IMarker { }

private interface IMarker { }

private class Filter : IEventFilter<IMarker>
{
public static bool Executed;

public Task Execute(IMarker @event, IEventFilterContext context, EventFilterExecutionDelegate next)
{
Executed = true;
return Task.CompletedTask;
}
}
}
}
10 changes: 4 additions & 6 deletions src/Handyman.Mediator/tests/EventPipelineTests.cs
@@ -1,8 +1,7 @@
using System.Threading;
using System.Threading.Tasks;
using Handyman.Mediator.Pipeline;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Handyman.Mediator.Tests
Expand Down Expand Up @@ -34,8 +33,7 @@ private class EventFilter : IEventFilter<Event>
{
public bool Executed { get; set; }

public Task Execute(EventPipelineContext<Event> context,
EventFilterExecutionDelegate next)
public Task Execute(Event @event, IEventFilterContext context, EventFilterExecutionDelegate next)
{
Executed = true;
return next();
Expand Down
Expand Up @@ -123,7 +123,7 @@ public EventFilter(CancellationTokenSource cancellationTokenSource)

public bool Executed { get; set; }

public Task Execute(EventPipelineContext<Event> context, EventFilterExecutionDelegate next)
public Task Execute(Event @event, IEventFilterContext context, EventFilterExecutionDelegate next)
{
_cancellationTokenSource.Cancel();
Executed = true;
Expand Down
40 changes: 24 additions & 16 deletions src/Handyman.Mediator/tests/Handyman.Mediator.Tests.csproj
@@ -1,22 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>net472;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Shouldly" Version="3.0.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lamar" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Shouldly" Version="3.0.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\Handyman.Mediator.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\Handyman.Mediator.csproj" />
</ItemGroup>

</Project>
Expand Up @@ -43,7 +43,7 @@ private class ToggleEnabledEventFilter : IEventFilter<Event>
{
public bool Executed { get; set; }

public Task Execute(EventPipelineContext<Event> context, EventFilterExecutionDelegate next)
public Task Execute(Event @event, IEventFilterContext context, EventFilterExecutionDelegate next)
{
Executed = true;
return next();
Expand All @@ -54,7 +54,7 @@ private class ToggleDisabledEventFilter : IEventFilter<Event>
{
public bool Executed { get; set; }

public Task Execute(EventPipelineContext<Event> context, EventFilterExecutionDelegate next)
public Task Execute(Event @event, IEventFilterContext context, EventFilterExecutionDelegate next)
{
Executed = true;
return next();
Expand All @@ -66,8 +66,8 @@ private class EventFilterToggle : IEventFilterToggle
public bool Enabled { get; set; }
public EventFilterToggleMetadata ToggleMetadata { get; set; }

public Task<bool> IsEnabled<TEvent>(EventFilterToggleMetadata toggleMetadata,
EventPipelineContext<TEvent> pipelineContext) where TEvent : IEvent
public Task<bool> IsEnabled<TEvent>(EventFilterToggleMetadata toggleMetadata, EventPipelineContext<TEvent> pipelineContext)
where TEvent : IEvent
{
ToggleMetadata = toggleMetadata;
return Task.FromResult(Enabled);
Expand Down
Expand Up @@ -64,8 +64,8 @@ private class EventHandlerToggle : IEventHandlerToggle
public bool Enabled { get; set; }
public EventHandlerToggleMetadata ToggleMetadata { get; set; }

public Task<bool> IsEnabled<TEvent>(EventHandlerToggleMetadata toggleMetadata,
EventPipelineContext<TEvent> pipelineContext) where TEvent : IEvent
public Task<bool> IsEnabled<TEvent>(EventHandlerToggleMetadata toggleMetadata, EventPipelineContext<TEvent> pipelineContext)
where TEvent : IEvent
{
ToggleMetadata = toggleMetadata;
return Task.FromResult(Enabled);
Expand Down
Expand Up @@ -42,7 +42,8 @@ public override void Configure(IEventPipelineBuilder builder, IServiceProvider s

private class EventFilterSelector : IEventFilterSelector
{
public Task SelectFilters<TEvent>(List<IEventFilter<TEvent>> filters, EventPipelineContext<TEvent> context) where TEvent : IEvent
public Task SelectFilters<TEvent>(List<IEventFilter<TEvent>> filters, EventPipelineContext<TEvent> context)
where TEvent : IEvent
{
filters.Add(new EventFilter<TEvent> { Action = context.ServiceProvider.GetRequiredService<Action<string>>() });
return Task.CompletedTask;
Expand All @@ -59,11 +60,10 @@ private class EventHandlerSelector : IEventHandlerSelector
}

private class EventFilter<TEvent> : IEventFilter<TEvent>
where TEvent : IEvent
{
public Action<string> Action { get; set; }

public Task Execute(EventPipelineContext<TEvent> context, EventFilterExecutionDelegate next)
public Task Execute(TEvent @event, IEventFilterContext context, EventFilterExecutionDelegate next)
{
Action("filter");
return next();
Expand Down

0 comments on commit 97e06ee

Please sign in to comment.