diff --git a/src/Ocelot/Cache/IOcelotCache.cs b/src/Ocelot/Cache/IOcelotCache.cs index 9ac26d8f..9abf5761 100644 --- a/src/Ocelot/Cache/IOcelotCache.cs +++ b/src/Ocelot/Cache/IOcelotCache.cs @@ -1,11 +1,13 @@ using System; +using System.Collections.Generic; namespace Ocelot.Cache { public interface IOcelotCache { - void Add(string key, T value, TimeSpan ttl); - void AddAndDelete(string key, T value, TimeSpan ttl); - T Get(string key); + void Add(string key, T value, TimeSpan ttl, string region); + void AddAndDelete(string key, T value, TimeSpan ttl, string region); + T Get(string key, string region); + void ClearRegion(string region); } } diff --git a/src/Ocelot/Cache/IRegionCreator.cs b/src/Ocelot/Cache/IRegionCreator.cs new file mode 100644 index 00000000..8ed186dd --- /dev/null +++ b/src/Ocelot/Cache/IRegionCreator.cs @@ -0,0 +1,9 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Cache +{ + public interface IRegionCreator + { + string Create(FileReRoute reRoute); + } +} \ No newline at end of file diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index c2cb019e..9c6dcf32 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) @@ -33,11 +37,11 @@ namespace Ocelot.Cache.Middleware return; } - var downstreamUrlKey = DownstreamRequest.RequestUri.OriginalString; + var downstreamUrlKey = $"{DownstreamRequest.Method.Method}-{DownstreamRequest.RequestUri.OriginalString}"; _logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey); - var cached = _outputCache.Get(downstreamUrlKey); + var cached = _outputCache.Get(downstreamUrlKey, DownstreamRoute.ReRoute.CacheOptions.Region); if (cached != null) { @@ -63,7 +67,7 @@ namespace Ocelot.Cache.Middleware var response = HttpResponseMessage; - _outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.FileCacheOptions.TtlSeconds)); + _outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.CacheOptions.TtlSeconds), DownstreamRoute.ReRoute.CacheOptions.Region); _logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey); } diff --git a/src/Ocelot/Cache/OcelotCacheManagerCache.cs b/src/Ocelot/Cache/OcelotCacheManagerCache.cs index e92d678d..33ee1543 100644 --- a/src/Ocelot/Cache/OcelotCacheManagerCache.cs +++ b/src/Ocelot/Cache/OcelotCacheManagerCache.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using CacheManager.Core; namespace Ocelot.Cache @@ -12,12 +14,12 @@ namespace Ocelot.Cache _cacheManager = cacheManager; } - public void Add(string key, T value, TimeSpan ttl) + public void Add(string key, T value, TimeSpan ttl, string region) { - _cacheManager.Add(new CacheItem(key, value, ExpirationMode.Absolute, ttl)); + _cacheManager.Add(new CacheItem(key, region, value, ExpirationMode.Absolute, ttl)); } - public void AddAndDelete(string key, T value, TimeSpan ttl) + public void AddAndDelete(string key, T value, TimeSpan ttl, string region) { var exists = _cacheManager.Get(key); @@ -26,12 +28,17 @@ namespace Ocelot.Cache _cacheManager.Remove(key); } - _cacheManager.Add(new CacheItem(key, value, ExpirationMode.Absolute, ttl)); + Add(key, value, ttl, region); } - public T Get(string key) + public T Get(string key, string region) { - return _cacheManager.Get(key); + return _cacheManager.Get(key, region); + } + + public void ClearRegion(string region) + { + _cacheManager.ClearRegion(region); } } } \ No newline at end of file diff --git a/src/Ocelot/Cache/RegionCreator.cs b/src/Ocelot/Cache/RegionCreator.cs new file mode 100644 index 00000000..87de751c --- /dev/null +++ b/src/Ocelot/Cache/RegionCreator.cs @@ -0,0 +1,24 @@ +using System.Linq; +using Ocelot.Configuration; +using Ocelot.Configuration.File; + +namespace Ocelot.Cache +{ + + public class RegionCreator : IRegionCreator + { + public string Create(FileReRoute reRoute) + { + if(!string.IsNullOrEmpty(reRoute?.FileCacheOptions?.Region)) + { + return reRoute?.FileCacheOptions?.Region; + } + + var methods = string.Join("", reRoute.UpstreamHttpMethod.Select(m => m)); + + var region = $"{methods}{reRoute.UpstreamPathTemplate.Replace("/", "")}"; + + return region; + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Cache/Regions.cs b/src/Ocelot/Cache/Regions.cs new file mode 100644 index 00000000..1dbefbd5 --- /dev/null +++ b/src/Ocelot/Cache/Regions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Ocelot.Cache +{ + public class Regions + { + public Regions(List value) + { + Value = value; + } + public List Value {get;private set;} + } +} \ No newline at end of file diff --git a/src/Ocelot/Configuration/CacheOptions.cs b/src/Ocelot/Configuration/CacheOptions.cs index 2fdaf2bb..a8b6a9d3 100644 --- a/src/Ocelot/Configuration/CacheOptions.cs +++ b/src/Ocelot/Configuration/CacheOptions.cs @@ -2,11 +2,13 @@ { public class CacheOptions { - public CacheOptions(int ttlSeconds) + public CacheOptions(int ttlSeconds, string region) { TtlSeconds = ttlSeconds; + Region = region; } public int TtlSeconds { get; private set; } + public string Region {get;private set;} } } diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index c44edd08..3b9c91d0 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Ocelot.Cache; using Ocelot.Configuration.Builder; using Ocelot.Configuration.File; using Ocelot.Configuration.Parser; @@ -36,6 +37,7 @@ namespace Ocelot.Configuration.Creator private IQoSOptionsCreator _qosOptionsCreator; private IReRouteOptionsCreator _fileReRouteOptionsCreator; private IRateLimitOptionsCreator _rateLimitOptionsCreator; + private IRegionCreator _regionCreator; public FileOcelotConfigurationCreator( IOptions options, @@ -52,9 +54,11 @@ namespace Ocelot.Configuration.Creator IServiceProviderConfigurationCreator serviceProviderConfigCreator, IQoSOptionsCreator qosOptionsCreator, IReRouteOptionsCreator fileReRouteOptionsCreator, - IRateLimitOptionsCreator rateLimitOptionsCreator + IRateLimitOptionsCreator rateLimitOptionsCreator, + IRegionCreator regionCreator ) { + _regionCreator = regionCreator; _rateLimitOptionsCreator = rateLimitOptionsCreator; _requestIdKeyCreator = requestIdKeyCreator; _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; @@ -137,6 +141,8 @@ namespace Ocelot.Configuration.Creator var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting); + var region = _regionCreator.Create(fileReRoute); + var reRoute = new ReRouteBuilder() .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) @@ -151,7 +157,7 @@ namespace Ocelot.Configuration.Creator .WithClaimsToQueries(claimsToQueries) .WithRequestIdKey(requestIdKey) .WithIsCached(fileReRouteOptions.IsCached) - .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)) + .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region)) .WithDownstreamScheme(fileReRoute.DownstreamScheme) .WithLoadBalancer(fileReRoute.LoadBalancer) .WithDownstreamHost(fileReRoute.DownstreamHost) diff --git a/src/Ocelot/Configuration/File/FileCacheOptions.cs b/src/Ocelot/Configuration/File/FileCacheOptions.cs index 3f86006b..df9fb631 100644 --- a/src/Ocelot/Configuration/File/FileCacheOptions.cs +++ b/src/Ocelot/Configuration/File/FileCacheOptions.cs @@ -3,5 +3,6 @@ public class FileCacheOptions { public int TtlSeconds { get; set; } + public string Region {get; set;} } } diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index a6673d09..2b8734c4 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -46,7 +46,7 @@ namespace Ocelot.Configuration IsAuthorised = isAuthorised; RequestIdKey = requestIdKey; IsCached = isCached; - FileCacheOptions = fileCacheOptions; + CacheOptions = fileCacheOptions; ClaimsToQueries = claimsToQueries ?? new List(); ClaimsToClaims = claimsToClaims @@ -74,7 +74,7 @@ namespace Ocelot.Configuration public Dictionary RouteClaimsRequirement { get; private set; } public string RequestIdKey { get; private set; } public bool IsCached { get; private set; } - public CacheOptions FileCacheOptions { get; private set; } + public CacheOptions CacheOptions { get; private set; } public string DownstreamScheme {get;private set;} public bool IsQos { get; private set; } public QoSOptions QosOptionsOptions { get; private set; } diff --git a/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs index 703d039b..c5413274 100644 --- a/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs @@ -30,7 +30,7 @@ namespace Ocelot.Configuration.Repository public async Task> Get() { - var config = _cache.Get(_ocelotConfiguration); + var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration); if (config != null) { @@ -68,7 +68,7 @@ namespace Ocelot.Configuration.Repository if (result.Response) { - _cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3)); + _cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), _ocelotConfiguration); return new OkResponse(); } diff --git a/src/Ocelot/Controllers/OutputCacheController.cs b/src/Ocelot/Controllers/OutputCacheController.cs new file mode 100644 index 00000000..a68201a1 --- /dev/null +++ b/src/Ocelot/Controllers/OutputCacheController.cs @@ -0,0 +1,29 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ocelot.Cache; +using Ocelot.Configuration.Provider; + +namespace Ocelot.Controllers +{ + [Authorize] + [Route("outputcache")] + public class OutputCacheController : Controller + { + private IOcelotCache _cache; + + public OutputCacheController(IOcelotCache cache) + { + _cache = cache; + } + + [HttpDelete] + [Route("{region}")] + public IActionResult Delete(string region) + { + _cache.ClearRegion(region); + return new NoContentResult(); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 14410379..954942dc 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -145,6 +145,7 @@ namespace Ocelot.DependencyInjection .AddJsonFormatters(); services.AddLogging(); + 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.IntegrationTests/AdministrationTests.cs b/test/Ocelot.IntegrationTests/AdministrationTests.cs index b03d587e..39721df6 100644 --- a/test/Ocelot.IntegrationTests/AdministrationTests.cs +++ b/test/Ocelot.IntegrationTests/AdministrationTests.cs @@ -7,6 +7,7 @@ using System.Net.Http.Headers; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using Ocelot.Cache; using Ocelot.Configuration.File; using Ocelot.ManualTest; using Shouldly; @@ -120,7 +121,12 @@ namespace Ocelot.IntegrationTests DownstreamScheme = "https", DownstreamPathTemplate = "/", UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Geoff" + } }, new FileReRoute() { @@ -129,7 +135,12 @@ namespace Ocelot.IntegrationTests DownstreamScheme = "https", DownstreamPathTemplate = "/", UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test" + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Dave" + } } } }; @@ -218,6 +229,57 @@ namespace Ocelot.IntegrationTests .BDDfy(); } + [Fact] + public void should_clear_region() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + AdministrationPath = "/administration" + }, + ReRoutes = new List() + { + new FileReRoute() + { + DownstreamHost = "localhost", + DownstreamPort = 80, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10 + } + }, + new FileReRoute() + { + DownstreamHost = "localhost", + DownstreamPort = 80, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10 + } + } + } + }; + + var regionToClear = "gettest"; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) + .BDDfy(); + } + private void GivenAnotherOcelotIsRunning(string baseUrl) { _httpClientTwo.BaseAddress = new Uri(baseUrl); @@ -256,6 +318,13 @@ namespace Ocelot.IntegrationTests _response = _httpClient.PostAsync(url, content).Result; } + private void ThenTheResponseShouldBe(List expected) + { + var content = _response.Content.ReadAsStringAsync().Result; + var result = JsonConvert.DeserializeObject(content); + result.Value.ShouldBe(expected); + } + private void ThenTheResponseShouldBe(FileConfiguration expected) { var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); @@ -353,6 +422,11 @@ namespace Ocelot.IntegrationTests _response = _httpClient.GetAsync(url).Result; } + private void WhenIDeleteOnTheApiGateway(string url) + { + _response = _httpClient.DeleteAsync(url).Result; + } + private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) { _response.StatusCode.ShouldBe(expectedHttpStatusCode); diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 59513b38..1556111a 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -30,6 +30,7 @@ + diff --git a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj index f541111f..57f23ad0 100644 --- a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj +++ b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj @@ -22,6 +22,7 @@ + diff --git a/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs b/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs index 87752a52..f097ccf1 100644 --- a/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs +++ b/test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using CacheManager.Core; using Moq; using Ocelot.Cache; @@ -16,20 +17,22 @@ namespace Ocelot.UnitTests.Cache private string _value; private string _resultGet; private TimeSpan _ttlSeconds; + private List _resultKeys; + private string _region; public CacheManagerCacheTests() { _mockCacheManager = new Mock>(); _ocelotOcelotCacheManager = new OcelotCacheManagerCache(_mockCacheManager.Object); } + [Fact] public void should_get_from_cache() { - this.Given(x => x.GivenTheFollowingIsCached("someKey", "someValue")) + this.Given(x => x.GivenTheFollowingIsCached("someKey", "someRegion", "someValue")) .When(x => x.WhenIGetFromTheCache()) .Then(x => x.ThenTheResultIs("someValue")) .BDDfy(); - } [Fact] @@ -40,13 +43,37 @@ namespace Ocelot.UnitTests.Cache .BDDfy(); } + [Fact] + public void should_delete_key_from_cache() + { + this.Given(_ => GivenTheFollowingRegion("fookey")) + .When(_ => WhenIDeleteTheRegion("fookey")) + .Then(_ => ThenTheRegionIsDeleted("fookey")) + .BDDfy(); + } + + private void WhenIDeleteTheRegion(string region) + { + _ocelotOcelotCacheManager.ClearRegion(region); + } + + private void ThenTheRegionIsDeleted(string region) + { + _mockCacheManager + .Verify(x => x.ClearRegion(region), Times.Once); + } + + private void GivenTheFollowingRegion(string key) + { + _ocelotOcelotCacheManager.Add(key, "doesnt matter", TimeSpan.FromSeconds(10), "region"); + } + private void WhenIAddToTheCache(string key, string value, TimeSpan ttlSeconds) { _key = key; _value = value; _ttlSeconds = ttlSeconds; - - _ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds); + _ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds, "region"); } private void ThenTheCacheIsCalledCorrectly() @@ -62,15 +89,16 @@ namespace Ocelot.UnitTests.Cache private void WhenIGetFromTheCache() { - _resultGet = _ocelotOcelotCacheManager.Get(_key); + _resultGet = _ocelotOcelotCacheManager.Get(_key, _region); } - private void GivenTheFollowingIsCached(string key, string value) + private void GivenTheFollowingIsCached(string key, string region, string value) { _key = key; _value = value; + _region = region; _mockCacheManager - .Setup(x => x.Get(It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(value); } } diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index dfc0220f..ff74fba0 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() @@ -90,7 +92,7 @@ namespace Ocelot.UnitTests.Cache { var reRoute = new ReRouteBuilder() .WithIsCached(true) - .WithCacheOptions(new CacheOptions(100)) + .WithCacheOptions(new CacheOptions(100, "kanken")) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -118,13 +120,13 @@ namespace Ocelot.UnitTests.Cache private void ThenTheCacheGetIsCalledCorrectly() { _cacheManager - .Verify(x => x.Get(It.IsAny()), Times.Once); + .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); } private void ThenTheCacheAddIsCalledCorrectly() { _cacheManager - .Verify(x => x.Add(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + .Verify(x => x.Add(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } private void GivenResponseIsNotCached() @@ -138,7 +140,7 @@ namespace Ocelot.UnitTests.Cache { _response = response; _cacheManager - .Setup(x => x.Get(It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(_response); } diff --git a/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs b/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs new file mode 100644 index 00000000..6a475425 --- /dev/null +++ b/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using Ocelot.Cache; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Configuration.File; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Cache +{ + public class RegionCreatorTests + { + private string _result; + private FileReRoute _reRoute; + + [Fact] + public void should_create_region() + { + var reRoute = new FileReRoute + { + UpstreamHttpMethod = new List { "Get" }, + UpstreamPathTemplate = "/testdummy" + }; + + this.Given(_ => GivenTheReRoute(reRoute)) + .When(_ => WhenICreateTheRegion()) + .Then(_ => ThenTheRegionIs("Gettestdummy")) + .BDDfy(); + } + + [Fact] + public void should_use_region() + { + var reRoute = new FileReRoute + { + FileCacheOptions = new FileCacheOptions + { + Region = "region" + } + }; + + this.Given(_ => GivenTheReRoute(reRoute)) + .When(_ => WhenICreateTheRegion()) + .Then(_ => ThenTheRegionIs("region")) + .BDDfy(); + } + + private void GivenTheReRoute(FileReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenICreateTheRegion() + { + RegionCreator regionCreator = new RegionCreator(); + _result = regionCreator.Create(_reRoute); + } + + private void ThenTheRegionIs(string expected) + { + _result.ShouldBe(expected); + } + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index bd8c46d3..386958c9 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; +using Ocelot.Cache; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Creator; @@ -39,6 +40,7 @@ namespace Ocelot.UnitTests.Configuration private Mock _qosOptionsCreator; private Mock _fileReRouteOptionsCreator; private Mock _rateLimitOptions; + private Mock _regionCreator; public FileConfigurationCreatorTests() { @@ -59,6 +61,7 @@ namespace Ocelot.UnitTests.Configuration _qosOptionsCreator = new Mock(); _fileReRouteOptionsCreator = new Mock(); _rateLimitOptions = new Mock(); + _regionCreator = new Mock(); _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( _fileConfig.Object, _validator.Object, _logger.Object, @@ -66,7 +69,51 @@ namespace Ocelot.UnitTests.Configuration _qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object, _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object, _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object, - _rateLimitOptions.Object); + _rateLimitOptions.Object, _regionCreator.Object); + } + + [Fact] + public void should_call_region_creator() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + FileCacheOptions = new FileCacheOptions + { + Region = "region" + } + } + }, + })) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingRegionIsReturned("region")) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheRegionCreatorIsCalledCorrectly("region")) + .BDDfy(); + } + + private void GivenTheFollowingRegionIsReturned(string region) + { + _regionCreator + .Setup(x => x.Create(It.IsAny())) + .Returns(region); + } + + private void ThenTheRegionCreatorIsCalledCorrectly(string expected) + { + _regionCreator + .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); } [Fact] diff --git a/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs b/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs new file mode 100644 index 00000000..100597a0 --- /dev/null +++ b/test/Ocelot.UnitTests/Controllers/OutputCacheControllerTests.cs @@ -0,0 +1,46 @@ +using Xunit; +using Shouldly; +using TestStack.BDDfy; +using Ocelot.Controllers; +using System; +using Moq; +using Ocelot.Cache; +using System.Net.Http; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; + +namespace Ocelot.UnitTests.Controllers +{ + public class OutputCacheControllerTests + { + private OutputCacheController _controller; + private Mock> _cache; + private IActionResult _result; + + public OutputCacheControllerTests() + { + _cache = new Mock>(); + _controller = new OutputCacheController(_cache.Object); + } + + [Fact] + public void should_delete_key() + { + this.When(_ => WhenIDeleteTheKey("a")) + .Then(_ => ThenTheKeyIsDeleted("a")) + .BDDfy(); + } + + private void ThenTheKeyIsDeleted(string key) + { + _result.ShouldBeOfType(); + _cache + .Verify(x => x.ClearRegion(key), Times.Once); + } + + private void WhenIDeleteTheKey(string key) + { + _result = _controller.Delete(key); + } + } +} \ No newline at end of file