diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index fb1ebce5..8b8b565d 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -13,16 +14,19 @@ namespace Ocelot.Cache.Middleware private readonly RequestDelegate _next; private readonly IOcelotLogger _logger; private readonly IOcelotCache _outputCache; + private readonly IRegionCreator _regionCreator; public OutputCacheMiddleware(RequestDelegate next, IOcelotLoggerFactory loggerFactory, IRequestScopedDataRepository scopedDataRepository, - IOcelotCache outputCache) + IOcelotCache outputCache, + IRegionCreator regionCreator) :base(scopedDataRepository) { _next = next; _outputCache = outputCache; _logger = loggerFactory.CreateLogger(); + _regionCreator = regionCreator; } public async Task Invoke(HttpContext context) @@ -63,7 +67,7 @@ namespace Ocelot.Cache.Middleware var response = HttpResponseMessage; - var region = $"{DownstreamRoute.ReRoute.UpstreamHttpMethod}-{DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}"; + var region = _regionCreator.Region(DownstreamRoute.ReRoute); _outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.FileCacheOptions.TtlSeconds), region); diff --git a/src/Ocelot/Cache/RegionBuilder.cs b/src/Ocelot/Cache/RegionBuilder.cs new file mode 100644 index 00000000..eb9972bc --- /dev/null +++ b/src/Ocelot/Cache/RegionBuilder.cs @@ -0,0 +1,22 @@ +using System.Linq; +using Ocelot.Configuration; + +namespace Ocelot.Cache +{ + public interface IRegionCreator + { + string Region(ReRoute reRoute); + } + + public class RegionCreator : IRegionCreator + { + public string Region(ReRoute reRoute) + { + var methods = string.Join(",", reRoute.UpstreamHttpMethod.Select(m => m.Method)); + + var region = $"{methods} {reRoute.UpstreamPathTemplate.Value}"; + + return region; + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Cache/RegionsGetter.cs b/src/Ocelot/Cache/RegionsGetter.cs new file mode 100644 index 00000000..1c331c2a --- /dev/null +++ b/src/Ocelot/Cache/RegionsGetter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Ocelot.Configuration.Provider; +using Ocelot.Logging; + +namespace Ocelot.Cache +{ + public interface IRegionsGetter + { + Task> Regions(); + } + public class RegionsGetter : IRegionsGetter + { + private readonly IOcelotConfigurationProvider _provider; + private readonly IRegionCreator _creator; + private readonly IOcelotLogger _logger; + + public RegionsGetter(IOcelotConfigurationProvider provider, IRegionCreator creator, IOcelotLoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + _provider = provider; + _creator = creator; + } + + public async Task> Regions() + { + var config = await _provider.Get(); + + if(config.IsError) + { + _logger.LogError("unable to find regions", new Exception(string.Join(",", config.Errors))); + return new List(); + } + + var regions = new List(); + + foreach(var reRoute in config.Data.ReRoutes) + { + var region = _creator.Region(reRoute); + regions.Add(region); + } + + return regions; + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Controllers/OutputCacheController.cs b/src/Ocelot/Controllers/OutputCacheController.cs index 2f56bf8d..04150a5f 100644 --- a/src/Ocelot/Controllers/OutputCacheController.cs +++ b/src/Ocelot/Controllers/OutputCacheController.cs @@ -2,6 +2,7 @@ using System.Net.Http; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Ocelot.Cache; +using Ocelot.Configuration.Provider; namespace Ocelot.Controllers { @@ -10,16 +11,19 @@ namespace Ocelot.Controllers public class OutputCacheController : Controller { private IOcelotCache _cache; + private IRegionsGetter _regionsGetter; - public OutputCacheController(IOcelotCache cache) + public OutputCacheController(IOcelotCache cache, IRegionsGetter regionsGetter) { _cache = cache; + _regionsGetter = regionsGetter; } [HttpGet] public IActionResult Get() { - return new NotFoundResult(); + var regions = _regionsGetter.Regions(); + return new OkObjectResult(regions); } [HttpDelete] diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 14410379..d2ff4b74 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -145,6 +145,8 @@ namespace Ocelot.DependencyInjection .AddJsonFormatters(); services.AddLogging(); + services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 08bbf527..eea05872 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -30,6 +30,7 @@ + diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index ab063b61..0039e2af 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -5,6 +5,7 @@ using System.Net.Http; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Moq; using Ocelot.Cache; using Ocelot.Cache.Middleware; @@ -43,6 +44,7 @@ namespace Ocelot.UnitTests.Cache x.AddLogging(); x.AddSingleton(_cacheManager.Object); x.AddSingleton(_scopedRepo.Object); + x.AddSingleton(); }) .UseUrls(_url) .UseKestrel() diff --git a/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs b/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs new file mode 100644 index 00000000..9d2b5a65 --- /dev/null +++ b/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using Ocelot.Cache; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Cache +{ + public class RegionCreatorTests + { + private string _result; + private ReRoute _reRoute; + + [Fact] + public void should_create_region() + { + var reRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod(new List{"Get"}) + .WithUpstreamPathTemplate("/test/dummy") + .Build(); + + this.Given(_ => GivenTheReRoute(reRoute)) + .When(_ => WhenICreateTheRegion()) + .Then(_ => ThenTheRegionIs("Get /test/dummy")) + .BDDfy(); + } + + private void GivenTheReRoute(ReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenICreateTheRegion() + { + RegionCreator regionCreator = new RegionCreator(); + _result = regionCreator.Region(_reRoute); + } + + private void ThenTheRegionIs(string expected) + { + _result.ShouldBe(expected); + } + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Cache/RegionsGetterTests.cs b/test/Ocelot.UnitTests/Cache/RegionsGetterTests.cs new file mode 100644 index 00000000..1f235daf --- /dev/null +++ b/test/Ocelot.UnitTests/Cache/RegionsGetterTests.cs @@ -0,0 +1,109 @@ +using Xunit; +using TestStack.BDDfy; +using Shouldly; +using Ocelot.Cache; +using Moq; +using Ocelot.Configuration.Provider; +using System.Collections.Generic; +using Ocelot.Responses; +using Ocelot.Configuration; +using System.Threading.Tasks; +using Ocelot.Configuration.Builder; +using System; +using Ocelot.Errors; +using Ocelot.Logging; + +namespace Ocelot.UnitTests.Cache +{ + public class RegionsGetterTests + { + private RegionsGetter _regionsGetter; + private readonly Mock _provider; + private readonly Mock _creator; + private readonly Mock _factory; + private List _result; + + public RegionsGetterTests() + { + _provider = new Mock(); + _creator = new Mock(); + _factory = new Mock(); + var logger = new Mock(); + _factory + .Setup(x => x.CreateLogger()) + .Returns(logger.Object); + _regionsGetter = new RegionsGetter(_provider.Object, _creator.Object, _factory.Object); + } + + [Fact] + public void should_get_regions() + { + var reRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod(new List{"Get"}) + .WithUpstreamPathTemplate("/") + .Build(); + + var reRoutes = new List + { + reRoute + }; + + var config = new OcelotConfiguration(reRoutes, "whocares!"); + + var expected = new List + { + "balls" + }; + + this.Given(_ => GivenTheFollowingConfig(config)) + .And(_ => GivenTheProviderReturns("balls")) + .When(_ => WhenIGetTheRegions()) + .Then(_ => ThenTheFollowingIsReturned(expected)) + .BDDfy(); + } + + [Fact] + public void should_return_empty_regions() + { + var expected = new List(); + + this.Given(_ => GivenAnErrorGettingTheConfig()) + .When(_ => WhenIGetTheRegions()) + .Then(_ => ThenTheFollowingIsReturned(expected)) + .BDDfy(); + } + + private void GivenAnErrorGettingTheConfig() + { + var config = new OcelotConfiguration(new List(), "whocares!"); + + _provider + .Setup(x => x.Get()) + .ReturnsAsync(new ErrorResponse(It.IsAny())); + } + + private void GivenTheProviderReturns(string expected) + { + _creator + .Setup(x => x.Region(It.IsAny())) + .Returns(expected); + } + + private void GivenTheFollowingConfig(IOcelotConfiguration config) + { + _provider + .Setup(x => x.Get()) + .ReturnsAsync(new OkResponse(config)); + } + + private void WhenIGetTheRegions() + { + _result = _regionsGetter.Regions().Result; + } + + private void ThenTheFollowingIsReturned(List expected) + { + _result.ShouldBe(expected); + } + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs b/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs index 5e2268af..fa43369e 100644 --- a/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs +++ b/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs @@ -15,12 +15,14 @@ namespace Ocelot.UnitTests.Controllers { private OutputCacheController _controller; private Mock> _cache; + private Mock _getter; private IActionResult _result; public OutputCacheControllerTests() { _cache = new Mock>(); - _controller = new OutputCacheController(_cache.Object); + _getter = new Mock(); + _controller = new OutputCacheController(_cache.Object, _getter.Object); } [Fact]