Skip to content

Commit

Permalink
Feature/inject error mapper (ThreeMammals#562)
Browse files Browse the repository at this point in the history
* added delegate to select last handler

* ThreeMammals#529 implemented a way we can inject the last delegating handler

* wip - moving code

* ThreeMammals#529 removed loads of qos code and moved it into Ocelot.Provider.Polly

* ThreeMammals#529 can now inject http client expcetions to ocelot errors mappers and updated docs
  • Loading branch information
TomPallister authored Aug 19, 2018
1 parent 98ba027 commit 6d8b18c
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 317 deletions.
17 changes: 16 additions & 1 deletion docs/features/qualityofservice.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,22 @@ Ocelot supports one QoS capability at the current time. You can set on a per ReR
want to use a circuit breaker when making requests to a downstream service. This uses the an awesome
.NET library called Polly check them out `here <https://github.com/App-vNext/Polly>`_.

Add the following section to a ReRoute configuration.
The first thing you need to do if you want to use the administration API is bring in the relavent NuGet package..

``Install-Package Ocelot.Provider.Polly``

Then in your ConfigureServices method

.. code-block:: csharp
public virtual void ConfigureServices(IServiceCollection services)
{
services
.AddOcelot()
.AddPolly();
}
Then add the following section to a ReRoute configuration.

.. code-block:: json
Expand Down
2 changes: 0 additions & 2 deletions src/Ocelot/Configuration/QoSOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ public QoSOptions(
int durationofBreak,
int timeoutValue,
string key,
//todo - this is never set in Ocelot so always Pessimistic...I guess it doesn't
//matter to much.
string timeoutStrategy = "Pessimistic")
{
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
Expand Down
1 change: 1 addition & 0 deletions src/Ocelot/DependencyInjection/OcelotBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo
Services.TryAddSingleton<IDownstreamRequestCreator, DownstreamRequestCreator>();
Services.TryAddSingleton<IFrameworkDescription, FrameworkDescription>();
Services.TryAddSingleton<IQoSFactory, QoSFactory>();
Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();
}

public IOcelotBuilder AddSingletonDefinedAggregator<T>()
Expand Down
10 changes: 7 additions & 3 deletions src/Ocelot/Requester/HttpClientHttpRequester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ public class HttpClientHttpRequester : IHttpRequester
private readonly IHttpClientCache _cacheHandlers;
private readonly IOcelotLogger _logger;
private readonly IDelegatingHandlerHandlerFactory _factory;
private readonly IExceptionToErrorMapper _mapper;

public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
IHttpClientCache cacheHandlers,
IDelegatingHandlerHandlerFactory house)
IDelegatingHandlerHandlerFactory factory,
IExceptionToErrorMapper mapper)
{
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
_cacheHandlers = cacheHandlers;
_factory = house;
_factory = factory;
_mapper = mapper;
}

public async Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext context)
Expand All @@ -35,7 +38,8 @@ public async Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext c
}
catch (Exception exception)
{
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
var error = _mapper.Map(exception);
return new ErrorResponse<HttpResponseMessage>(error);
}
finally
{
Expand Down
29 changes: 29 additions & 0 deletions src/Ocelot/Requester/HttpExeptionToErrorMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Ocelot.Requester
{
using System;
using System.Collections.Generic;
using Errors;
using Microsoft.Extensions.DependencyInjection;

public class HttpExeptionToErrorMapper : IExceptionToErrorMapper
{
private readonly Dictionary<Type, Func<Exception, Error>> _mappers;

public HttpExeptionToErrorMapper(IServiceProvider serviceProvider)
{
_mappers = serviceProvider.GetService<Dictionary<Type, Func<Exception, Error>>>();
}

public Error Map(Exception exception)
{
var type = exception.GetType();

if (_mappers != null && _mappers.ContainsKey(type))
{
return _mappers[type](exception);
}

return new UnableToCompleteRequestError(exception);
}
}
}
10 changes: 10 additions & 0 deletions src/Ocelot/Requester/IExceptionToErrorMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using Ocelot.Errors;

namespace Ocelot.Requester
{
public interface IExceptionToErrorMapper
{
Error Map(Exception exception);
}
}
10 changes: 0 additions & 10 deletions src/Ocelot/Requester/ReRouteDelegatingHandler.cs

This file was deleted.

12 changes: 0 additions & 12 deletions src/Ocelot/Requester/UnableToFindDelegatingHandlerProviderError.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class HttpClientHttpRequesterTest
private DownstreamContext _request;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
private Mock<IExceptionToErrorMapper> _mapper;

public HttpClientHttpRequesterTest()
{
Expand All @@ -38,10 +39,12 @@ public HttpClientHttpRequesterTest()
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
.Returns(_logger.Object);
_cacheHandlers = new Mock<IHttpClientCache>();
_mapper = new Mock<IExceptionToErrorMapper>();
_httpClientRequester = new HttpClientHttpRequester(
_loggerFactory.Object,
_cacheHandlers.Object,
_factory.Object);
_factory.Object,
_mapper.Object);
}

[Fact]
Expand Down Expand Up @@ -144,6 +147,7 @@ private void ThenTheResponseIsCalledError()

private void ThenTheErrorIsTimeout()
{
_mapper.Verify(x => x.Map(It.IsAny<Exception>()), Times.Once);
_response.Errors[0].ShouldBeOfType<UnableToCompleteRequestError>();
}

Expand All @@ -165,6 +169,8 @@ private void GivenTheHouseReturnsTimeoutHandler()
};

_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));

_mapper.Setup(x => x.Map(It.IsAny<Exception>())).Returns(new UnableToCompleteRequestError(new Exception()));
}

class OkDelegatingHandler : DelegatingHandler
Expand Down
52 changes: 52 additions & 0 deletions test/Ocelot.UnitTests/Requester/HttpExeptionToErrorMapperTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace Ocelot.UnitTests.Requester
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Errors;
using Ocelot.Requester;
using Responder;
using Shouldly;
using Xunit;

public class HttpExeptionToErrorMapperTests
{
private HttpExeptionToErrorMapper _mapper;
private readonly ServiceCollection _services;

public HttpExeptionToErrorMapperTests()
{
_services = new ServiceCollection();
var provider = _services.BuildServiceProvider();
_mapper = new HttpExeptionToErrorMapper(provider);
}

[Fact]
public void should_return_default_error_because_mappers_are_null()
{
var error = _mapper.Map(new Exception());

error.ShouldBeOfType<UnableToCompleteRequestError>();
}

[Fact]
public void should_return_error_from_mapper()
{
var errorMapping = new Dictionary<Type, Func<Exception, Error>>
{
{typeof(TaskCanceledException), e => new AnyError()},
};

_services.AddSingleton(errorMapping);

var provider = _services.BuildServiceProvider();

_mapper = new HttpExeptionToErrorMapper(provider);

var error = _mapper.Map(new TaskCanceledException());

error.ShouldBeOfType<AnyError>();
}
}
}
55 changes: 55 additions & 0 deletions test/Ocelot.UnitTests/Requester/QoSFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
namespace Ocelot.UnitTests.Requester
{
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Shouldly;
using Xunit;

public class QoSFactoryTests
{
private QoSFactory _factory;
private ServiceCollection _services;
private readonly Mock<IOcelotLoggerFactory> _loggerFactory;

public QoSFactoryTests()
{
_services = new ServiceCollection();
_loggerFactory = new Mock<IOcelotLoggerFactory>();
var provider = _services.BuildServiceProvider();
_factory = new QoSFactory(provider, _loggerFactory.Object);
}

[Fact]
public void should_return_error()
{
var downstreamReRoute = new DownstreamReRouteBuilder().Build();
var handler = _factory.Get(downstreamReRoute);
handler.IsError.ShouldBeTrue();
handler.Errors[0].ShouldBeOfType<UnableToFindQoSProviderError>();
}

[Fact]
public void should_return_handler()
{
_services = new ServiceCollection();
DelegatingHandler QosDelegatingHandlerDelegate(DownstreamReRoute a, IOcelotLoggerFactory b) => new FakeDelegatingHandler();
_services.AddSingleton<QosDelegatingHandlerDelegate>(QosDelegatingHandlerDelegate);
var provider = _services.BuildServiceProvider();
_factory = new QoSFactory(provider, _loggerFactory.Object);
var downstreamReRoute = new DownstreamReRouteBuilder().Build();
var handler = _factory.Get(downstreamReRoute);
handler.IsError.ShouldBeFalse();
handler.Data.ShouldBeOfType<FakeDelegatingHandler>();
}

class FakeDelegatingHandler : DelegatingHandler
{
}
}
}
93 changes: 0 additions & 93 deletions test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs

This file was deleted.

Loading

0 comments on commit 6d8b18c

Please sign in to comment.