diff --git a/src/Ocelot.Library/Builder/ReRouteBuilder.cs b/src/Ocelot.Library/Configuration/Builder/ReRouteBuilder.cs similarity index 97% rename from src/Ocelot.Library/Builder/ReRouteBuilder.cs rename to src/Ocelot.Library/Configuration/Builder/ReRouteBuilder.cs index 2cf9538d..c6f1e90a 100644 --- a/src/Ocelot.Library/Builder/ReRouteBuilder.cs +++ b/src/Ocelot.Library/Configuration/Builder/ReRouteBuilder.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; -using Ocelot.Library.Configuration; -namespace Ocelot.Library.Builder +namespace Ocelot.Library.Configuration.Builder { public class ReRouteBuilder { diff --git a/src/Ocelot.Library/Configuration/Creator/IOcelotConfigurationCreator.cs b/src/Ocelot.Library/Configuration/Creator/IOcelotConfigurationCreator.cs new file mode 100644 index 00000000..1465c23f --- /dev/null +++ b/src/Ocelot.Library/Configuration/Creator/IOcelotConfigurationCreator.cs @@ -0,0 +1,9 @@ +using Ocelot.Library.Responses; + +namespace Ocelot.Library.Configuration.Creator +{ + public interface IOcelotConfigurationCreator + { + Response Create(); + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Configuration/OcelotConfiguration.cs b/src/Ocelot.Library/Configuration/OcelotConfiguration.cs index ae1cf189..c6fc0786 100644 --- a/src/Ocelot.Library/Configuration/OcelotConfiguration.cs +++ b/src/Ocelot.Library/Configuration/OcelotConfiguration.cs @@ -1,127 +1,14 @@ -using System; -using System.Linq; -using Microsoft.Extensions.Logging; -using Ocelot.Library.RequestBuilder; +using System.Collections.Generic; namespace Ocelot.Library.Configuration { - using System.Collections.Generic; - using Microsoft.Extensions.Options; - using Yaml; - public class OcelotConfiguration : IOcelotConfiguration { - private readonly IOptions _options; - private readonly IConfigurationValidator _configurationValidator; - private readonly List _reRoutes; - private const string RegExMatchEverything = ".*"; - private const string RegExMatchEndString = "$"; - private readonly IClaimToHeaderConfigurationParser _claimToHeaderConfigurationParser; - private readonly ILogger _logger; - - public OcelotConfiguration(IOptions options, - IConfigurationValidator configurationValidator, - IClaimToHeaderConfigurationParser claimToHeaderConfigurationParser, - ILogger logger) + public OcelotConfiguration(List reRoutes) { - _options = options; - _configurationValidator = configurationValidator; - _claimToHeaderConfigurationParser = claimToHeaderConfigurationParser; - _logger = logger; - _reRoutes = new List(); - SetUpConfiguration(); + ReRoutes = reRoutes; } - /// - /// This method is meant to be tempoary to convert a yaml config to an ocelot config...probably wont keep this but we will see - /// will need a refactor at some point as its crap - /// - private void SetUpConfiguration() - { - var response = _configurationValidator.IsValid(_options.Value); - - if (!response.IsError && !response.Data.IsError) - { - foreach (var reRoute in _options.Value.ReRoutes) - { - SetUpReRoute(reRoute); - } - } - } - - private void SetUpReRoute(YamlReRoute reRoute) - { - var upstreamTemplate = reRoute.UpstreamTemplate; - - var placeholders = new List(); - - for (var i = 0; i < upstreamTemplate.Length; i++) - { - if (IsPlaceHolder(upstreamTemplate, i)) - { - var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i); - var difference = postitionOfPlaceHolderClosingBracket - i + 1; - var variableName = upstreamTemplate.Substring(i, difference); - placeholders.Add(variableName); - } - } - - foreach (var placeholder in placeholders) - { - upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything); - } - - upstreamTemplate = $"{upstreamTemplate}{RegExMatchEndString}"; - - var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider); - - if (isAuthenticated) - { - var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider, - reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName, - reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes, - reRoute.AuthenticationOptions.ScopeSecret); - - var configHeaders = GetHeadersToAddToRequest(reRoute); - - _reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, - reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, - authOptionsForRoute, configHeaders - )); - } - else - { - _reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod, - upstreamTemplate, isAuthenticated, null, new List())); - } - } - - private List GetHeadersToAddToRequest(YamlReRoute reRoute) - { - var configHeaders = new List(); - - foreach (var add in reRoute.AddHeadersToRequest) - { - var configurationHeader = _claimToHeaderConfigurationParser.Extract(add.Key, add.Value); - - if (configurationHeader.IsError) - { - _logger.LogCritical(new EventId(1, "Application Failed to start"), - $"Unable to extract configuration for key: {add.Key} and value: {add.Value} your configuration file is incorrect"); - - throw new Exception(configurationHeader.Errors[0].Message); - } - configHeaders.Add(configurationHeader.Data); - } - - return configHeaders; - } - - private bool IsPlaceHolder(string upstreamTemplate, int i) - { - return upstreamTemplate[i] == '{'; - } - - public List ReRoutes => _reRoutes; + public List ReRoutes { get; } } } \ No newline at end of file diff --git a/src/Ocelot.Library/Configuration/ClaimToHeaderConfigurationParser.cs b/src/Ocelot.Library/Configuration/Parser/ClaimToHeaderConfigurationParser.cs similarity index 98% rename from src/Ocelot.Library/Configuration/ClaimToHeaderConfigurationParser.cs rename to src/Ocelot.Library/Configuration/Parser/ClaimToHeaderConfigurationParser.cs index c81c5468..e2aa94d5 100644 --- a/src/Ocelot.Library/Configuration/ClaimToHeaderConfigurationParser.cs +++ b/src/Ocelot.Library/Configuration/Parser/ClaimToHeaderConfigurationParser.cs @@ -5,7 +5,7 @@ using Ocelot.Library.Errors; using Ocelot.Library.RequestBuilder; using Ocelot.Library.Responses; -namespace Ocelot.Library.Configuration +namespace Ocelot.Library.Configuration.Parser { public class ClaimToHeaderConfigurationParser : IClaimToHeaderConfigurationParser { diff --git a/src/Ocelot.Library/Configuration/IClaimToHeaderConfigurationParser.cs b/src/Ocelot.Library/Configuration/Parser/IClaimToHeaderConfigurationParser.cs similarity index 79% rename from src/Ocelot.Library/Configuration/IClaimToHeaderConfigurationParser.cs rename to src/Ocelot.Library/Configuration/Parser/IClaimToHeaderConfigurationParser.cs index ad627ba5..633cfcc6 100644 --- a/src/Ocelot.Library/Configuration/IClaimToHeaderConfigurationParser.cs +++ b/src/Ocelot.Library/Configuration/Parser/IClaimToHeaderConfigurationParser.cs @@ -1,6 +1,6 @@ using Ocelot.Library.Responses; -namespace Ocelot.Library.Configuration +namespace Ocelot.Library.Configuration.Parser { public interface IClaimToHeaderConfigurationParser { diff --git a/src/Ocelot.Library/Configuration/Provider/IOcelotConfigurationProvider.cs b/src/Ocelot.Library/Configuration/Provider/IOcelotConfigurationProvider.cs new file mode 100644 index 00000000..6918ca6d --- /dev/null +++ b/src/Ocelot.Library/Configuration/Provider/IOcelotConfigurationProvider.cs @@ -0,0 +1,9 @@ +using Ocelot.Library.Responses; + +namespace Ocelot.Library.Configuration.Provider +{ + public interface IOcelotConfigurationProvider + { + Response Get(); + } +} diff --git a/src/Ocelot.Library/Configuration/Repository/IOcelotConfigurationRepository.cs b/src/Ocelot.Library/Configuration/Repository/IOcelotConfigurationRepository.cs new file mode 100644 index 00000000..bd99149c --- /dev/null +++ b/src/Ocelot.Library/Configuration/Repository/IOcelotConfigurationRepository.cs @@ -0,0 +1,10 @@ +using Ocelot.Library.Responses; + +namespace Ocelot.Library.Configuration.Repository +{ + public interface IOcelotConfigurationRepository + { + Response Get(); + Response AddOrReplace(IOcelotConfiguration ocelotConfiguration); + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs b/src/Ocelot.Library/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs new file mode 100644 index 00000000..f590ce8b --- /dev/null +++ b/src/Ocelot.Library/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs @@ -0,0 +1,29 @@ +using Ocelot.Library.Responses; + +namespace Ocelot.Library.Configuration.Repository +{ + /// + /// Register as singleton + /// + public class InMemoryOcelotConfigurationRepository : IOcelotConfigurationRepository + { + private static readonly object LockObject = new object(); + + private IOcelotConfiguration _ocelotConfiguration; + + public Response Get() + { + return new OkResponse(_ocelotConfiguration); + } + + public Response AddOrReplace(IOcelotConfiguration ocelotConfiguration) + { + lock (LockObject) + { + _ocelotConfiguration = ocelotConfiguration; + } + + return new OkResponse(); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Configuration/Yaml/YamlOcelotConfigurationCreator.cs b/src/Ocelot.Library/Configuration/Yaml/YamlOcelotConfigurationCreator.cs new file mode 100644 index 00000000..5d6179a0 --- /dev/null +++ b/src/Ocelot.Library/Configuration/Yaml/YamlOcelotConfigurationCreator.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Ocelot.Library.Configuration.Creator; +using Ocelot.Library.Configuration.Parser; +using Ocelot.Library.Configuration.Repository; +using Ocelot.Library.Errors; +using Ocelot.Library.Responses; + +namespace Ocelot.Library.Configuration.Yaml +{ + /// + /// Register as singleton + /// + public class YamlOcelotConfigurationCreator : IOcelotConfigurationCreator + { + private readonly IOptions _options; + private readonly IConfigurationValidator _configurationValidator; + private const string RegExMatchEverything = ".*"; + private const string RegExMatchEndString = "$"; + private readonly IClaimToHeaderConfigurationParser _claimToHeaderConfigurationParser; + private readonly ILogger _logger; + + public YamlOcelotConfigurationCreator( + IOptions options, + IConfigurationValidator configurationValidator, + IClaimToHeaderConfigurationParser claimToHeaderConfigurationParser, + ILogger logger) + { + _options = options; + _configurationValidator = configurationValidator; + _claimToHeaderConfigurationParser = claimToHeaderConfigurationParser; + _logger = logger; + } + + public Response Create() + { + var config = SetUpConfiguration(); + + return new OkResponse(config); + } + + /// + /// This method is meant to be tempoary to convert a yaml config to an ocelot config...probably wont keep this but we will see + /// will need a refactor at some point as its crap + /// + private IOcelotConfiguration SetUpConfiguration() + { + var response = _configurationValidator.IsValid(_options.Value); + + var reRoutes = new List(); + + if (!response.IsError && !response.Data.IsError) + { + + foreach (var yamlReRoute in _options.Value.ReRoutes) + { + var ocelotReRoute = SetUpReRoute(yamlReRoute); + reRoutes.Add(ocelotReRoute); + } + } + + return new OcelotConfiguration(reRoutes); + } + + private ReRoute SetUpReRoute(YamlReRoute reRoute) + { + var upstreamTemplate = reRoute.UpstreamTemplate; + + var placeholders = new List(); + + for (var i = 0; i < upstreamTemplate.Length; i++) + { + if (IsPlaceHolder(upstreamTemplate, i)) + { + var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i); + var difference = postitionOfPlaceHolderClosingBracket - i + 1; + var variableName = upstreamTemplate.Substring(i, difference); + placeholders.Add(variableName); + } + } + + foreach (var placeholder in placeholders) + { + upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything); + } + + upstreamTemplate = $"{upstreamTemplate}{RegExMatchEndString}"; + + var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider); + + if (isAuthenticated) + { + var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider, + reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName, + reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes, + reRoute.AuthenticationOptions.ScopeSecret); + + var configHeaders = GetHeadersToAddToRequest(reRoute); + + return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, + reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, + authOptionsForRoute, configHeaders + ); + } + + return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod, + upstreamTemplate, isAuthenticated, null, new List()); + } + + private List GetHeadersToAddToRequest(YamlReRoute reRoute) + { + var configHeaders = new List(); + + foreach (var add in reRoute.AddHeadersToRequest) + { + var configurationHeader = _claimToHeaderConfigurationParser.Extract(add.Key, add.Value); + + if (configurationHeader.IsError) + { + _logger.LogCritical(new EventId(1, "Application Failed to start"), + $"Unable to extract configuration for key: {add.Key} and value: {add.Value} your configuration file is incorrect"); + + throw new Exception(configurationHeader.Errors[0].Message); + } + configHeaders.Add(configurationHeader.Data); + } + + return configHeaders; + } + + private bool IsPlaceHolder(string upstreamTemplate, int i) + { + return upstreamTemplate[i] == '{'; + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Configuration/Yaml/YamlOcelotConfigurationProvider.cs b/src/Ocelot.Library/Configuration/Yaml/YamlOcelotConfigurationProvider.cs new file mode 100644 index 00000000..534fe83c --- /dev/null +++ b/src/Ocelot.Library/Configuration/Yaml/YamlOcelotConfigurationProvider.cs @@ -0,0 +1,49 @@ +using Ocelot.Library.Configuration.Creator; +using Ocelot.Library.Configuration.Provider; +using Ocelot.Library.Configuration.Repository; +using Ocelot.Library.Responses; + +namespace Ocelot.Library.Configuration.Yaml +{ + /// + /// Register as singleton + /// + public class YamlOcelotConfigurationProvider : IOcelotConfigurationProvider + { + private readonly IOcelotConfigurationRepository _repo; + private readonly IOcelotConfigurationCreator _creator; + + public YamlOcelotConfigurationProvider(IOcelotConfigurationRepository repo, + IOcelotConfigurationCreator creator) + { + _repo = repo; + _creator = creator; + } + + public Response Get() + { + var config = _repo.Get(); + + if (config.IsError) + { + return new ErrorResponse(config.Errors); + } + + if (config.Data == null) + { + var configuration = _creator.Create(); + + if (configuration.IsError) + { + return new ErrorResponse(configuration.Errors); + } + + _repo.AddOrReplace(configuration.Data); + + return new OkResponse(configuration.Data); + } + + return new OkResponse(config.Data); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot.Library/DependencyInjection/ServiceCollectionExtensions.cs index 19f80900..882163c1 100644 --- a/src/Ocelot.Library/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot.Library/DependencyInjection/ServiceCollectionExtensions.cs @@ -1,4 +1,9 @@ -namespace Ocelot.Library.DependencyInjection +using Ocelot.Library.Configuration.Creator; +using Ocelot.Library.Configuration.Parser; +using Ocelot.Library.Configuration.Provider; +using Ocelot.Library.Configuration.Repository; + +namespace Ocelot.Library.DependencyInjection { using Authentication; using Configuration; @@ -16,22 +21,30 @@ public static class ServiceCollectionExtensions { - public static IServiceCollection AddOcelot(this IServiceCollection services, IConfigurationRoot configurationRoot) + public static IServiceCollection AddOcelotYamlConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot) { - // framework services - services.AddOptions(); - services.AddMvcCore().AddJsonFormatters(); - services.AddLogging(); - // initial configuration from yaml services.Configure(configurationRoot); // ocelot services. - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + + return services; + } + + public static IServiceCollection AddOcelot(this IServiceCollection services) + { + // framework services + services.AddMvcCore().AddJsonFormatters(); + services.AddLogging(); + + // ocelot services. + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Ocelot.Library/DownstreamRouteFinder/DownstreamRouteFinder.cs b/src/Ocelot.Library/DownstreamRouteFinder/DownstreamRouteFinder.cs index 5962f808..542f7341 100644 --- a/src/Ocelot.Library/DownstreamRouteFinder/DownstreamRouteFinder.cs +++ b/src/Ocelot.Library/DownstreamRouteFinder/DownstreamRouteFinder.cs @@ -1,29 +1,31 @@ -namespace Ocelot.Library.DownstreamRouteFinder -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Configuration; - using Errors; - using Responses; - using UrlMatcher; +using System; +using System.Collections.Generic; +using System.Linq; +using Ocelot.Library.Configuration.Provider; +using Ocelot.Library.Errors; +using Ocelot.Library.Responses; +using Ocelot.Library.UrlMatcher; +namespace Ocelot.Library.DownstreamRouteFinder +{ public class DownstreamRouteFinder : IDownstreamRouteFinder { - private readonly IOcelotConfiguration _configuration; + private readonly IOcelotConfigurationProvider _configProvider; private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; private readonly ITemplateVariableNameAndValueFinder _templateVariableNameAndValueFinder; - public DownstreamRouteFinder(IOcelotConfiguration configuration, IUrlPathToUrlTemplateMatcher urlMatcher, ITemplateVariableNameAndValueFinder templateVariableNameAndValueFinder) + public DownstreamRouteFinder(IOcelotConfigurationProvider configProvider, IUrlPathToUrlTemplateMatcher urlMatcher, ITemplateVariableNameAndValueFinder templateVariableNameAndValueFinder) { - _configuration = configuration; + _configProvider = configProvider; _urlMatcher = urlMatcher; _templateVariableNameAndValueFinder = templateVariableNameAndValueFinder; } public Response FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod) { - foreach (var template in _configuration.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase))) + var configuration = _configProvider.Get(); + + foreach (var template in configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase))) { var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplatePattern); diff --git a/src/Ocelot/Startup.cs b/src/Ocelot/Startup.cs index 66989428..ae9c59cd 100644 --- a/src/Ocelot/Startup.cs +++ b/src/Ocelot/Startup.cs @@ -28,7 +28,8 @@ namespace Ocelot // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddOcelot(Configuration); + services.AddOcelotYamlConfiguration(Configuration); + services.AddOcelot(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs new file mode 100644 index 00000000..c46942da --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ocelot.Library.Configuration; +using Ocelot.Library.Configuration.Builder; +using Ocelot.Library.Configuration.Repository; +using Ocelot.Library.Responses; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + public class InMemoryConfigurationRepositoryTests + { + private readonly InMemoryOcelotConfigurationRepository _repo; + private IOcelotConfiguration _config; + private Response _result; + private Response _getResult; + + public InMemoryConfigurationRepositoryTests() + { + _repo = new InMemoryOcelotConfigurationRepository(); + } + + [Fact] + public void can_add_config() + { + this.Given(x => x.GivenTheConfigurationIs(new FakeConfig("initial"))) + .When(x => x.WhenIAddOrReplaceTheConfig()) + .Then(x => x.ThenNoErrorsAreReturned()) + .BDDfy(); + } + + [Fact] + public void can_get_config() + { + this.Given(x => x.GivenThereIsASavedConfiguration()) + .When(x => x.WhenIGetTheConfiguration()) + .Then(x => x.ThenTheConfigurationIsReturned()) + .BDDfy(); + } + + /// + /// long runnnig unit test to make sure repo thread safeok on + /// + [Fact] + public void repo_is_thread_safe() + { + var tasks = new Task[100000]; + for (int i = 0; i < tasks.Length; i++) + { + tasks[i] = Fire(); + } + + Task.WaitAll(tasks); + } + + private async Task Fire() + { + var taskGuid = Guid.NewGuid().ToString(); + _repo.AddOrReplace(new FakeConfig(taskGuid)); + var configuration = _repo.Get(); + configuration.Data.ReRoutes[0].DownstreamTemplate.ShouldBe(taskGuid); + } + + private void ThenTheConfigurationIsReturned() + { + _getResult.Data.ReRoutes[0].DownstreamTemplate.ShouldBe("initial"); + } + + private void WhenIGetTheConfiguration() + { + _getResult = _repo.Get(); + } + + private void GivenThereIsASavedConfiguration() + { + GivenTheConfigurationIs(new FakeConfig("initial")); + WhenIAddOrReplaceTheConfig(); + } + + private void GivenTheConfigurationIs(IOcelotConfiguration config) + { + _config = config; + } + + private void WhenIAddOrReplaceTheConfig() + { + _result = _repo.AddOrReplace(_config); + } + + private void ThenNoErrorsAreReturned() + { + _result.IsError.ShouldBeFalse(); + } + + class FakeConfig : IOcelotConfiguration + { + private readonly string _downstreamTemplate; + + public FakeConfig(string downstreamTemplate) + { + _downstreamTemplate = downstreamTemplate; + } + + public List ReRoutes => new List + { + new ReRouteBuilder().WithDownstreamTemplate(_downstreamTemplate).Build() + }; + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/OcelotConfigurationTests.cs b/test/Ocelot.UnitTests/Configuration/YamlConfigurationCreatorTests.cs similarity index 88% rename from test/Ocelot.UnitTests/Configuration/OcelotConfigurationTests.cs rename to test/Ocelot.UnitTests/Configuration/YamlConfigurationCreatorTests.cs index 8846d60d..4e6d5fa5 100644 --- a/test/Ocelot.UnitTests/Configuration/OcelotConfigurationTests.cs +++ b/test/Ocelot.UnitTests/Configuration/YamlConfigurationCreatorTests.cs @@ -2,6 +2,9 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; +using Ocelot.Library.Configuration.Builder; +using Ocelot.Library.Configuration.Parser; +using Ocelot.Library.Configuration.Repository; using Ocelot.Library.RequestBuilder; using Shouldly; using TestStack.BDDfy; @@ -9,26 +12,28 @@ using Xunit; namespace Ocelot.UnitTests.Configuration { - using Library.Builder; using Library.Configuration; using Library.Configuration.Yaml; using Library.Responses; - public class OcelotConfigurationTests + public class YamlConfigurationCreatorTests { private readonly Mock> _yamlConfig; private readonly Mock _validator; - private OcelotConfiguration _config; + private Response _config; private YamlConfiguration _yamlConfiguration; - private readonly Mock _configExtractor; - private readonly Mock> _logger; + private readonly Mock _configParser; + private readonly Mock> _logger; + private readonly YamlOcelotConfigurationCreator _ocelotConfigurationCreator; - public OcelotConfigurationTests() + public YamlConfigurationCreatorTests() { - _logger = new Mock>(); - _configExtractor = new Mock(); + _logger = new Mock>(); + _configParser = new Mock(); _validator = new Mock(); _yamlConfig = new Mock>(); + _ocelotConfigurationCreator = new YamlOcelotConfigurationCreator( + _yamlConfig.Object, _validator.Object, _configParser.Object, _logger.Object); } [Fact] @@ -47,7 +52,7 @@ namespace Ocelot.UnitTests.Configuration } })) .And(x => x.GivenTheYamlConfigIsValid()) - .When(x => x.WhenIInstanciateTheOcelotConfig()) + .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() @@ -109,7 +114,7 @@ namespace Ocelot.UnitTests.Configuration })) .And(x => x.GivenTheYamlConfigIsValid()) .And(x => x.GivenTheConfigHeaderExtractorReturns(new ClaimToHeader("CustomerId", "CustomerId", "", 0))) - .When(x => x.WhenIInstanciateTheOcelotConfig()) + .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected)) .BDDfy(); @@ -117,7 +122,7 @@ namespace Ocelot.UnitTests.Configuration private void GivenTheConfigHeaderExtractorReturns(ClaimToHeader expected) { - _configExtractor + _configParser .Setup(x => x.Extract(It.IsAny(), It.IsAny())) .Returns(new OkResponse(expected)); } @@ -162,7 +167,7 @@ namespace Ocelot.UnitTests.Configuration } })) .And(x => x.GivenTheYamlConfigIsValid()) - .When(x => x.WhenIInstanciateTheOcelotConfig()) + .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected)) .BDDfy(); @@ -184,7 +189,7 @@ namespace Ocelot.UnitTests.Configuration } })) .And(x => x.GivenTheYamlConfigIsValid()) - .When(x => x.WhenIInstanciateTheOcelotConfig()) + .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() @@ -213,7 +218,7 @@ namespace Ocelot.UnitTests.Configuration } })) .And(x => x.GivenTheYamlConfigIsValid()) - .When(x => x.WhenIInstanciateTheOcelotConfig()) + .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() @@ -242,7 +247,7 @@ namespace Ocelot.UnitTests.Configuration } })) .And(x => x.GivenTheYamlConfigIsValid()) - .When(x => x.WhenIInstanciateTheOcelotConfig()) + .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() @@ -270,17 +275,16 @@ namespace Ocelot.UnitTests.Configuration .Returns(_yamlConfiguration); } - private void WhenIInstanciateTheOcelotConfig() + private void WhenICreateTheConfig() { - _config = new OcelotConfiguration(_yamlConfig.Object, _validator.Object, - _configExtractor.Object, _logger.Object); + _config = _ocelotConfigurationCreator.Create(); } private void ThenTheReRoutesAre(List expectedReRoutes) { - for (int i = 0; i < _config.ReRoutes.Count; i++) + for (int i = 0; i < _config.Data.ReRoutes.Count; i++) { - var result = _config.ReRoutes[i]; + var result = _config.Data.ReRoutes[i]; var expected = expectedReRoutes[i]; result.DownstreamTemplate.ShouldBe(expected.DownstreamTemplate); @@ -292,9 +296,9 @@ namespace Ocelot.UnitTests.Configuration private void ThenTheAuthenticationOptionsAre(List expectedReRoutes) { - for (int i = 0; i < _config.ReRoutes.Count; i++) + for (int i = 0; i < _config.Data.ReRoutes.Count; i++) { - var result = _config.ReRoutes[i].AuthenticationOptions; + var result = _config.Data.ReRoutes[i].AuthenticationOptions; var expected = expectedReRoutes[i].AuthenticationOptions; result.AdditionalScopes.ShouldBe(expected.AdditionalScopes); diff --git a/test/Ocelot.UnitTests/Configuration/YamlConfigurationProviderTests.cs b/test/Ocelot.UnitTests/Configuration/YamlConfigurationProviderTests.cs new file mode 100644 index 00000000..ae81c4f7 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/YamlConfigurationProviderTests.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Moq; +using Ocelot.Library.Configuration; +using Ocelot.Library.Configuration.Creator; +using Ocelot.Library.Configuration.Provider; +using Ocelot.Library.Configuration.Repository; +using Ocelot.Library.Configuration.Yaml; +using Ocelot.Library.Errors; +using Ocelot.Library.Responses; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + public class YamlConfigurationProviderTests + { + private readonly IOcelotConfigurationProvider _ocelotConfigurationProvider; + private readonly Mock _configurationRepository; + private readonly Mock _creator; + private Response _result; + + public YamlConfigurationProviderTests() + { + _creator = new Mock(); + _configurationRepository = new Mock(); + _ocelotConfigurationProvider = new YamlOcelotConfigurationProvider(_configurationRepository.Object, _creator.Object); + } + + [Fact] + public void should_get_config() + { + this.Given(x => x.GivenTheRepoReturns(new OkResponse(new OcelotConfiguration(new List())))) + .When(x => x.WhenIGetTheConfig()) + .Then(x => x.TheFollowingIsReturned(new OkResponse(new OcelotConfiguration(new List())))) + .BDDfy(); + } + + [Fact] + public void should_create_config_if_it_doesnt_exist() + { + this.Given(x => x.GivenTheRepoReturns(new OkResponse(null))) + .And(x => x.GivenTheCreatorReturns(new OkResponse(new OcelotConfiguration(new List())))) + .When(x => x.WhenIGetTheConfig()) + .Then(x => x.TheFollowingIsReturned(new OkResponse(new OcelotConfiguration(new List())))) + .BDDfy(); + } + + [Fact] + public void should_return_error() + { + this.Given(x => x.GivenTheRepoReturns(new ErrorResponse(new List + { + new AnyError() + }))) + .When(x => x.WhenIGetTheConfig()) + .Then(x => x.TheFollowingIsReturned( + new ErrorResponse(new List + { + new AnyError() + }))) + .BDDfy(); + } + + [Fact] + public void should_return_error_if_creator_errors() + { + this.Given(x => x.GivenTheRepoReturns(new OkResponse(null))) + .And(x => x.GivenTheCreatorReturns(new ErrorResponse(new List + { + new AnyError() + }))) + .When(x => x.WhenIGetTheConfig()) + .Then(x => x.TheFollowingIsReturned(new ErrorResponse(new List + { + new AnyError() + }))) + .BDDfy(); + } + + private void GivenTheCreatorReturns(Response config) + { + _creator + .Setup(x => x.Create()) + .Returns(config); + } + + private void GivenTheRepoReturns(Response config) + { + _configurationRepository + .Setup(x => x.Get()) + .Returns(config); + } + + private void WhenIGetTheConfig() + { + _result = _ocelotConfigurationProvider.Get(); + } + + private void TheFollowingIsReturned(Response expected) + { + _result.IsError.ShouldBe(expected.IsError); + } + + class AnyError : Error + { + public AnyError() + : base("blamo", OcelotErrorCode.UnknownError) + { + } + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 0f979b18..24ef25e7 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using Moq; +using Ocelot.Library.Configuration.Builder; +using Ocelot.Library.Configuration.Provider; using Shouldly; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.DownstreamRouteFinder { - using Library.Builder; using Library.Configuration; using Library.DownstreamRouteFinder; using Library.Responses; @@ -15,7 +16,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder public class DownstreamRouteFinderTests { private readonly IDownstreamRouteFinder _downstreamRouteFinder; - private readonly Mock _mockConfig; + private readonly Mock _mockConfig; private readonly Mock _mockMatcher; private readonly Mock _finder; private string _upstreamUrlPath; @@ -26,7 +27,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder public DownstreamRouteFinderTests() { - _mockConfig = new Mock(); + _mockConfig = new Mock(); _mockMatcher = new Mock(); _finder = new Mock(); _downstreamRouteFinder = new DownstreamRouteFinder(_mockConfig.Object, _mockMatcher.Object, _finder.Object); @@ -157,8 +158,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder { _reRoutesConfig = reRoutesConfig; _mockConfig - .Setup(x => x.ReRoutes) - .Returns(_reRoutesConfig); + .Setup(x => x.Get()) + .Returns(new OkResponse(new OcelotConfiguration(_reRoutesConfig))); } private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) diff --git a/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs b/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs index b9350cce..eebccfa3 100644 --- a/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs @@ -6,13 +6,13 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Moq; +using Ocelot.Library.Configuration.Builder; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.Middleware { using Library.Authentication; - using Library.Builder; using Library.DownstreamRouteFinder; using Library.Middleware; using Library.Repository; diff --git a/test/Ocelot.UnitTests/Middleware/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/Middleware/DownstreamRouteFinderMiddlewareTests.cs index 894852d8..898e1648 100644 --- a/test/Ocelot.UnitTests/Middleware/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Middleware/DownstreamRouteFinderMiddlewareTests.cs @@ -6,12 +6,12 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Moq; +using Ocelot.Library.Configuration.Builder; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.Middleware { - using Library.Builder; using Library.DownstreamRouteFinder; using Library.Middleware; using Library.Repository; diff --git a/test/Ocelot.UnitTests/Middleware/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/Middleware/DownstreamUrlCreatorMiddlewareTests.cs index f97766d9..56c0493c 100644 --- a/test/Ocelot.UnitTests/Middleware/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Middleware/DownstreamUrlCreatorMiddlewareTests.cs @@ -1,10 +1,11 @@ -namespace Ocelot.UnitTests.Middleware +using Ocelot.Library.Configuration.Builder; + +namespace Ocelot.UnitTests.Middleware { using System; using System.Collections.Generic; using System.IO; using System.Net.Http; - using Library.Builder; using Library.DownstreamRouteFinder; using Library.Middleware; using Library.Repository; diff --git a/test/Ocelot.UnitTests/Middleware/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Middleware/HttpRequestHeadersBuilderMiddlewareTests.cs index f04efbfc..ec0104cd 100644 --- a/test/Ocelot.UnitTests/Middleware/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Middleware/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -7,8 +7,8 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Moq; -using Ocelot.Library.Builder; using Ocelot.Library.Configuration; +using Ocelot.Library.Configuration.Builder; using Ocelot.Library.DownstreamRouteFinder; using Ocelot.Library.Middleware; using Ocelot.Library.Repository; diff --git a/test/Ocelot.UnitTests/RequestBuilder/ConfigurationHeadersExtractorTests.cs b/test/Ocelot.UnitTests/RequestBuilder/ConfigurationHeadersExtractorTests.cs index 48e93b8a..d61daaa7 100644 --- a/test/Ocelot.UnitTests/RequestBuilder/ConfigurationHeadersExtractorTests.cs +++ b/test/Ocelot.UnitTests/RequestBuilder/ConfigurationHeadersExtractorTests.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Ocelot.Library.Configuration; +using Ocelot.Library.Configuration.Parser; using Ocelot.Library.Errors; using Ocelot.Library.RequestBuilder; using Ocelot.Library.Responses; diff --git a/test/Ocelot.UnitTests/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs b/test/Ocelot.UnitTests/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs index 85f65d48..3db2b611 100644 --- a/test/Ocelot.UnitTests/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs +++ b/test/Ocelot.UnitTests/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; +using Ocelot.Library.Configuration.Builder; using Shouldly; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.UrlTemplateReplacer { - using Library.Builder; using Library.DownstreamRouteFinder; using Library.Responses; using Library.UrlMatcher;