mirror of
https://github.com/Radarr/Radarr.git
synced 2024-08-16 23:39:44 +02:00
fixed service registration for event handlers and executors.
This commit is contained in:
parent
399c96c5e3
commit
fa8f67d7fe
@ -5,7 +5,7 @@ module.exports = function (grunt) {
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
|
||||
curl: {
|
||||
'UI/JsLibraries/backbone.js' : 'http://backbonejs.org/backbone.js',
|
||||
'UI/JsLibraries/backbone.js' : 'http://documentcloud.github.io/backbone/backbone.js',
|
||||
'UI/JsLibraries/backbone.marionette.js' : 'http://marionettejs.com/downloads/backbone.marionette.js',
|
||||
'UI/JsLibraries/backbone.modelbinder.js' : 'http://raw.github.com/theironcook/Backbone.ModelBinder/master/Backbone.ModelBinder.js',
|
||||
'UI/JsLibraries/backbone.mutators.js' : 'http://raw.github.com/asciidisco/Backbone.Mutators/master/backbone.mutators.js',
|
||||
|
@ -23,13 +23,13 @@ private CommandResource RunCommand(CommandResource resource)
|
||||
{
|
||||
var commandType = _commands.Single(c => c.GetType().Name.Replace("Command", "").Equals(resource.Command, StringComparison.InvariantCultureIgnoreCase))
|
||||
.GetType();
|
||||
var command = (object)Request.Body.FromJson<ICommand>(commandType);
|
||||
var method = typeof(IMessageAggregator).GetMethod("PublishCommand");
|
||||
var genericMethod = method.MakeGenericMethod(commandType);
|
||||
genericMethod.Invoke(_messageAggregator, new[] { command });
|
||||
|
||||
|
||||
var command = Request.Body.FromJson<ICommand>(commandType);
|
||||
|
||||
_messageAggregator.PublishCommand(command);
|
||||
|
||||
return resource;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Test.Common;
|
||||
using FluentAssertions;
|
||||
using TinyIoC;
|
||||
|
||||
namespace NzbDrone.App.Test
|
||||
{
|
||||
@ -28,5 +31,24 @@ public void should_be_able_to_resolve_downlodclients()
|
||||
{
|
||||
MainAppContainerBuilder.BuildContainer().Resolve<IEnumerable<IDownloadClient>>().Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void container_should_inject_itself()
|
||||
{
|
||||
var factory = MainAppContainerBuilder.BuildContainer().Resolve<IServiceFactory>();
|
||||
|
||||
factory.Build<IIndexerService>().Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_resolve_command_executor_by_name()
|
||||
{
|
||||
var genericExecutor = typeof(IExecute<>).MakeGenericType(typeof(RssSyncCommand));
|
||||
|
||||
var executor = MainAppContainerBuilder.BuildContainer().Resolve(genericExecutor);
|
||||
|
||||
executor.Should().NotBeNull();
|
||||
executor.Should().BeAssignableTo<IExecute<RssSyncCommand>>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Messaging;
|
||||
@ -8,31 +7,35 @@
|
||||
namespace NzbDrone.Common.Test.EventingTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MessageAggregatorCommandTests : TestBase
|
||||
public class MessageAggregatorCommandTests : TestBase<MessageAggregator>
|
||||
{
|
||||
private Mock<IExecute<CommandA>> _executorA;
|
||||
private Mock<IExecute<CommandB>> _executorB;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_executorA = new Mock<IExecute<CommandA>>();
|
||||
_executorB = new Mock<IExecute<CommandB>>();
|
||||
|
||||
Mocker.GetMock<IServiceFactory>()
|
||||
.Setup(c => c.Build(typeof(IExecute<CommandA>)))
|
||||
.Returns(_executorA.Object);
|
||||
|
||||
Mocker.GetMock<IServiceFactory>()
|
||||
.Setup(c => c.Build(typeof(IExecute<CommandB>)))
|
||||
.Returns(_executorB.Object);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_publish_command_to_executor()
|
||||
{
|
||||
var commandA = new CommandA();
|
||||
|
||||
var executor = new Mock<IExecute<CommandA>>();
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { executor.Object });
|
||||
aggregator.PublishCommand(commandA);
|
||||
Subject.PublishCommand(commandA);
|
||||
|
||||
executor.Verify(c => c.Execute(commandA), Times.Once());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_more_than_one_handler()
|
||||
{
|
||||
var commandA = new CommandA();
|
||||
|
||||
var executor1 = new Mock<IExecute<CommandA>>();
|
||||
var executor2 = new Mock<IExecute<CommandA>>();
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { executor1.Object, executor2.Object });
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => aggregator.PublishCommand(commandA));
|
||||
_executorA.Verify(c => c.Execute(commandA), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -40,14 +43,11 @@ public void should_not_publish_to_incompatible_executor()
|
||||
{
|
||||
var commandA = new CommandA();
|
||||
|
||||
var executor1 = new Mock<IExecute<CommandA>>();
|
||||
var executor2 = new Mock<IExecute<CommandB>>();
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { executor1.Object, executor2.Object });
|
||||
|
||||
aggregator.PublishCommand(commandA);
|
||||
Subject.PublishCommand(commandA);
|
||||
|
||||
executor1.Verify(c => c.Execute(commandA), Times.Once());
|
||||
executor2.Verify(c => c.Execute(It.IsAny<CommandB>()), Times.Never());
|
||||
_executorA.Verify(c => c.Execute(commandA), Times.Once());
|
||||
_executorB.Verify(c => c.Execute(It.IsAny<CommandB>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -55,13 +55,10 @@ public void broken_executor_should_throw_the_exception()
|
||||
{
|
||||
var commandA = new CommandA();
|
||||
|
||||
var executor = new Mock<IExecute<CommandA>>();
|
||||
|
||||
executor.Setup(c => c.Execute(It.IsAny<CommandA>()))
|
||||
_executorA.Setup(c => c.Execute(It.IsAny<CommandA>()))
|
||||
.Throws(new NotImplementedException());
|
||||
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { executor.Object });
|
||||
Assert.Throws<NotImplementedException>(() => aggregator.PublishCommand(commandA));
|
||||
Assert.Throws<NotImplementedException>(() => Subject.PublishCommand(commandA));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,33 +8,42 @@
|
||||
namespace NzbDrone.Common.Test.EventingTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MessageAggregatorEventTests : TestBase
|
||||
public class MessageAggregatorEventTests : TestBase<MessageAggregator>
|
||||
{
|
||||
private Mock<IHandle<EventA>> HandlerA1;
|
||||
private Mock<IHandle<EventA>> HandlerA2;
|
||||
|
||||
private Mock<IHandle<EventB>> HandlerB1;
|
||||
private Mock<IHandle<EventB>> HandlerB2;
|
||||
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
HandlerA1 = new Mock<IHandle<EventA>>();
|
||||
HandlerA2 = new Mock<IHandle<EventA>>();
|
||||
HandlerB1 = new Mock<IHandle<EventB>>();
|
||||
HandlerB2 = new Mock<IHandle<EventB>>();
|
||||
|
||||
Mocker.GetMock<IServiceFactory>()
|
||||
.Setup(c => c.BuildAll<IHandle<EventA>>())
|
||||
.Returns(new List<IHandle<EventA>> { HandlerA1.Object, HandlerA2.Object });
|
||||
|
||||
Mocker.GetMock<IServiceFactory>()
|
||||
.Setup(c => c.BuildAll<IHandle<EventB>>())
|
||||
.Returns(new List<IHandle<EventB>> { HandlerB1.Object, HandlerB2.Object });
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_publish_event_to_handlers()
|
||||
{
|
||||
var eventA = new EventA();
|
||||
|
||||
Subject.PublishEvent(eventA);
|
||||
|
||||
var intHandler = new Mock<IHandle<EventA>>();
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { intHandler.Object });
|
||||
aggregator.PublishEvent(eventA);
|
||||
|
||||
intHandler.Verify(c => c.Handle(eventA), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_publish_to_more_than_one_handler()
|
||||
{
|
||||
var eventA = new EventA();
|
||||
|
||||
var intHandler1 = new Mock<IHandle<EventA>>();
|
||||
var intHandler2 = new Mock<IHandle<EventA>>();
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { intHandler1.Object, intHandler2.Object });
|
||||
aggregator.PublishEvent(eventA);
|
||||
|
||||
intHandler1.Verify(c => c.Handle(eventA), Times.Once());
|
||||
intHandler2.Verify(c => c.Handle(eventA), Times.Once());
|
||||
HandlerA1.Verify(c => c.Handle(eventA), Times.Once());
|
||||
HandlerA2.Verify(c => c.Handle(eventA), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -42,14 +51,14 @@ public void should_not_publish_to_incompatible_handlers()
|
||||
{
|
||||
var eventA = new EventA();
|
||||
|
||||
var aHandler = new Mock<IHandle<EventA>>();
|
||||
var bHandler = new Mock<IHandle<EventB>>();
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { aHandler.Object, bHandler.Object });
|
||||
|
||||
aggregator.PublishEvent(eventA);
|
||||
Subject.PublishEvent(eventA);
|
||||
|
||||
aHandler.Verify(c => c.Handle(eventA), Times.Once());
|
||||
bHandler.Verify(c => c.Handle(It.IsAny<EventB>()), Times.Never());
|
||||
HandlerA1.Verify(c => c.Handle(eventA), Times.Once());
|
||||
HandlerA2.Verify(c => c.Handle(eventA), Times.Once());
|
||||
|
||||
HandlerB1.Verify(c => c.Handle(It.IsAny<EventB>()), Times.Never());
|
||||
HandlerB2.Verify(c => c.Handle(It.IsAny<EventB>()), Times.Never());
|
||||
}
|
||||
|
||||
|
||||
@ -58,19 +67,14 @@ public void broken_handler_should_not_effect_others_handler()
|
||||
{
|
||||
var eventA = new EventA();
|
||||
|
||||
var intHandler1 = new Mock<IHandle<EventA>>();
|
||||
var intHandler2 = new Mock<IHandle<EventA>>();
|
||||
var intHandler3 = new Mock<IHandle<EventA>>();
|
||||
|
||||
intHandler2.Setup(c => c.Handle(It.IsAny<EventA>()))
|
||||
HandlerA1.Setup(c => c.Handle(It.IsAny<EventA>()))
|
||||
.Throws(new NotImplementedException());
|
||||
|
||||
var aggregator = new MessageAggregator(TestLogger, () => new List<IProcessMessage> { intHandler1.Object, intHandler2.Object , intHandler3.Object});
|
||||
aggregator.PublishEvent(eventA);
|
||||
|
||||
intHandler1.Verify(c => c.Handle(eventA), Times.Once());
|
||||
intHandler3.Verify(c => c.Handle(eventA), Times.Once());
|
||||
Subject.PublishEvent(eventA);
|
||||
|
||||
HandlerA1.Verify(c => c.Handle(eventA), Times.Once());
|
||||
HandlerA2.Verify(c => c.Handle(eventA), Times.Once());
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using TinyIoC;
|
||||
|
||||
namespace NzbDrone.Common
|
||||
@ -27,9 +28,16 @@ protected ContainerBuilderBase(params string[] assemblies)
|
||||
|
||||
private void AutoRegisterInterfaces()
|
||||
{
|
||||
var interfaces = _loadedTypes.Where(t => t.IsInterface);
|
||||
var simpleInterfaces = _loadedTypes.Where(t => t.IsInterface).ToList();
|
||||
var appliedInterfaces = _loadedTypes.SelectMany(t => t.GetInterfaces()).Where(i => i.Assembly.FullName.Contains("NzbDrone")).ToList();
|
||||
|
||||
foreach (var contract in interfaces)
|
||||
var contracts = simpleInterfaces.Union(appliedInterfaces)
|
||||
.Except(new List<Type> { typeof(IMessage), typeof(ICommand), typeof(IEvent) });
|
||||
|
||||
|
||||
var count = contracts.Count();
|
||||
|
||||
foreach (var contract in simpleInterfaces.Union(contracts))
|
||||
{
|
||||
AutoRegisterImplementations(contract);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
|
||||
@ -9,24 +8,20 @@ namespace NzbDrone.Common.Messaging
|
||||
public class MessageAggregator : IMessageAggregator
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly Func<IEnumerable<IProcessMessage>> _handlerFactory;
|
||||
private readonly IServiceFactory _serviceFactory;
|
||||
|
||||
public MessageAggregator(Logger logger, Func<IEnumerable<IProcessMessage>> handlers)
|
||||
public MessageAggregator(Logger logger, IServiceFactory serviceFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_handlerFactory = handlers;
|
||||
_serviceFactory = serviceFactory;
|
||||
}
|
||||
|
||||
public void PublishEvent<TEvent>(TEvent @event) where TEvent : IEvent
|
||||
{
|
||||
_logger.Trace("Publishing {0}", @event.GetType().Name);
|
||||
|
||||
|
||||
var handlers = _handlerFactory().ToList();
|
||||
|
||||
|
||||
//call synchronous handlers first.
|
||||
foreach (var handler in handlers.OfType<IHandle<TEvent>>())
|
||||
foreach (var handler in _serviceFactory.BuildAll<IHandle<TEvent>>())
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -40,7 +35,7 @@ public void PublishEvent<TEvent>(TEvent @event) where TEvent : IEvent
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var handler in handlers.OfType<IHandleAsync<TEvent>>())
|
||||
foreach (var handler in _serviceFactory.BuildAll<IHandleAsync<TEvent>>())
|
||||
{
|
||||
var handlerLocal = handler;
|
||||
Task.Factory.StartNew(() =>
|
||||
@ -55,10 +50,27 @@ public void PublishEvent<TEvent>(TEvent @event) where TEvent : IEvent
|
||||
|
||||
public void PublishCommand<TCommand>(TCommand command) where TCommand : ICommand
|
||||
{
|
||||
var handlerContract = typeof(IExecute<>).MakeGenericType(command.GetType());
|
||||
|
||||
_logger.Trace("Publishing {0}", command.GetType().Name);
|
||||
var handler = _handlerFactory().OfType<IExecute<TCommand>>().Single();
|
||||
|
||||
var handler = _serviceFactory.Build(handlerContract);
|
||||
|
||||
_logger.Debug("{0} -> {1}", command.GetType().Name, handler.GetType().Name);
|
||||
handler.Execute(command);
|
||||
|
||||
try
|
||||
{
|
||||
handlerContract.GetMethod("Execute").Invoke(handler, new object[] { command });
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
throw e.InnerException;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
_logger.Debug("{0} <- {1}", command.GetType().Name, handler.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
17
NzbDrone.Common/Messaging/MessageExtensions.cs
Normal file
17
NzbDrone.Common/Messaging/MessageExtensions.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Common.Messaging
|
||||
{
|
||||
public static class MessageExtensions
|
||||
{
|
||||
public static string GetExecutorName(this Type commandType)
|
||||
{
|
||||
if (!typeof(ICommand).IsAssignableFrom(commandType))
|
||||
{
|
||||
throw new ArgumentException("commandType must implement IExecute");
|
||||
}
|
||||
|
||||
return string.Format("I{0}Executor", commandType.Name);
|
||||
}
|
||||
}
|
||||
}
|
@ -116,7 +116,9 @@
|
||||
<Compile Include="Expansive\TreeNodeList.cs" />
|
||||
<Compile Include="IJsonSerializer.cs" />
|
||||
<Compile Include="Instrumentation\VersionLayoutRenderer.cs" />
|
||||
<Compile Include="Messaging\MessageExtensions.cs" />
|
||||
<Compile Include="Reflection\ReflectionExtensions.cs" />
|
||||
<Compile Include="ServiceFactory.cs" />
|
||||
<Compile Include="StringExtention.cs" />
|
||||
<Compile Include="HttpProvider.cs" />
|
||||
<Compile Include="ConfigFileProvider.cs" />
|
||||
@ -161,9 +163,6 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Expansive\license.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<WCFMetadata Include="Service References\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
39
NzbDrone.Common/ServiceFactory.cs
Normal file
39
NzbDrone.Common/ServiceFactory.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using TinyIoC;
|
||||
|
||||
namespace NzbDrone.Common
|
||||
{
|
||||
public interface IServiceFactory
|
||||
{
|
||||
T Build<T>() where T : class;
|
||||
IEnumerable<T> BuildAll<T>() where T : class;
|
||||
object Build(Type contract);
|
||||
}
|
||||
|
||||
public class ServiceFactory : IServiceFactory
|
||||
{
|
||||
private readonly TinyIoCContainer _container;
|
||||
|
||||
public ServiceFactory(TinyIoCContainer container)
|
||||
{
|
||||
_container = container;
|
||||
}
|
||||
|
||||
public T Build<T>() where T : class
|
||||
{
|
||||
return _container.Resolve<T>();
|
||||
}
|
||||
|
||||
public IEnumerable<T> BuildAll<T>() where T : class
|
||||
{
|
||||
return _container.ResolveAll<T>();
|
||||
}
|
||||
|
||||
public object Build(Type contract)
|
||||
{
|
||||
return _container.Resolve(contract);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Api.RootFolders;
|
||||
|
||||
namespace NzbDrone.Integration.Test
|
||||
{
|
||||
@ -13,10 +11,8 @@ public void should_have_built_in_indexer()
|
||||
{
|
||||
var indexers = Indexers.All();
|
||||
|
||||
|
||||
indexers.Should().NotBeEmpty();
|
||||
indexers.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FluentAssertions">
|
||||
|
Loading…
Reference in New Issue
Block a user