refactoring configuration code so its not so crazy, still need to work on the creator class

This commit is contained in:
TomPallister 2016-10-18 18:32:34 +01:00
parent 707f1d6908
commit 2e6640c6ef
23 changed files with 561 additions and 178 deletions

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Ocelot.Library.Configuration;
namespace Ocelot.Library.Builder namespace Ocelot.Library.Configuration.Builder
{ {
public class ReRouteBuilder public class ReRouteBuilder
{ {

View File

@ -0,0 +1,9 @@
using Ocelot.Library.Responses;
namespace Ocelot.Library.Configuration.Creator
{
public interface IOcelotConfigurationCreator
{
Response<IOcelotConfiguration> Create();
}
}

View File

@ -1,127 +1,14 @@
using System; using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Ocelot.Library.RequestBuilder;
namespace Ocelot.Library.Configuration namespace Ocelot.Library.Configuration
{ {
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using Yaml;
public class OcelotConfiguration : IOcelotConfiguration public class OcelotConfiguration : IOcelotConfiguration
{ {
private readonly IOptions<YamlConfiguration> _options; public OcelotConfiguration(List<ReRoute> reRoutes)
private readonly IConfigurationValidator _configurationValidator;
private readonly List<ReRoute> _reRoutes;
private const string RegExMatchEverything = ".*";
private const string RegExMatchEndString = "$";
private readonly IClaimToHeaderConfigurationParser _claimToHeaderConfigurationParser;
private readonly ILogger<OcelotConfiguration> _logger;
public OcelotConfiguration(IOptions<YamlConfiguration> options,
IConfigurationValidator configurationValidator,
IClaimToHeaderConfigurationParser claimToHeaderConfigurationParser,
ILogger<OcelotConfiguration> logger)
{ {
_options = options; ReRoutes = reRoutes;
_configurationValidator = configurationValidator;
_claimToHeaderConfigurationParser = claimToHeaderConfigurationParser;
_logger = logger;
_reRoutes = new List<ReRoute>();
SetUpConfiguration();
} }
/// <summary> public List<ReRoute> ReRoutes { get; }
/// 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
/// </summary>
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<string>();
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<ClaimToHeader>()));
}
}
private List<ClaimToHeader> GetHeadersToAddToRequest(YamlReRoute reRoute)
{
var configHeaders = new List<ClaimToHeader>();
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<ReRoute> ReRoutes => _reRoutes;
} }
} }

View File

@ -5,7 +5,7 @@ using Ocelot.Library.Errors;
using Ocelot.Library.RequestBuilder; using Ocelot.Library.RequestBuilder;
using Ocelot.Library.Responses; using Ocelot.Library.Responses;
namespace Ocelot.Library.Configuration namespace Ocelot.Library.Configuration.Parser
{ {
public class ClaimToHeaderConfigurationParser : IClaimToHeaderConfigurationParser public class ClaimToHeaderConfigurationParser : IClaimToHeaderConfigurationParser
{ {

View File

@ -1,6 +1,6 @@
using Ocelot.Library.Responses; using Ocelot.Library.Responses;
namespace Ocelot.Library.Configuration namespace Ocelot.Library.Configuration.Parser
{ {
public interface IClaimToHeaderConfigurationParser public interface IClaimToHeaderConfigurationParser
{ {

View File

@ -0,0 +1,9 @@
using Ocelot.Library.Responses;
namespace Ocelot.Library.Configuration.Provider
{
public interface IOcelotConfigurationProvider
{
Response<IOcelotConfiguration> Get();
}
}

View File

@ -0,0 +1,10 @@
using Ocelot.Library.Responses;
namespace Ocelot.Library.Configuration.Repository
{
public interface IOcelotConfigurationRepository
{
Response<IOcelotConfiguration> Get();
Response AddOrReplace(IOcelotConfiguration ocelotConfiguration);
}
}

View File

@ -0,0 +1,29 @@
using Ocelot.Library.Responses;
namespace Ocelot.Library.Configuration.Repository
{
/// <summary>
/// Register as singleton
/// </summary>
public class InMemoryOcelotConfigurationRepository : IOcelotConfigurationRepository
{
private static readonly object LockObject = new object();
private IOcelotConfiguration _ocelotConfiguration;
public Response<IOcelotConfiguration> Get()
{
return new OkResponse<IOcelotConfiguration>(_ocelotConfiguration);
}
public Response AddOrReplace(IOcelotConfiguration ocelotConfiguration)
{
lock (LockObject)
{
_ocelotConfiguration = ocelotConfiguration;
}
return new OkResponse();
}
}
}

View File

@ -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
{
/// <summary>
/// Register as singleton
/// </summary>
public class YamlOcelotConfigurationCreator : IOcelotConfigurationCreator
{
private readonly IOptions<YamlConfiguration> _options;
private readonly IConfigurationValidator _configurationValidator;
private const string RegExMatchEverything = ".*";
private const string RegExMatchEndString = "$";
private readonly IClaimToHeaderConfigurationParser _claimToHeaderConfigurationParser;
private readonly ILogger<YamlOcelotConfigurationCreator> _logger;
public YamlOcelotConfigurationCreator(
IOptions<YamlConfiguration> options,
IConfigurationValidator configurationValidator,
IClaimToHeaderConfigurationParser claimToHeaderConfigurationParser,
ILogger<YamlOcelotConfigurationCreator> logger)
{
_options = options;
_configurationValidator = configurationValidator;
_claimToHeaderConfigurationParser = claimToHeaderConfigurationParser;
_logger = logger;
}
public Response<IOcelotConfiguration> Create()
{
var config = SetUpConfiguration();
return new OkResponse<IOcelotConfiguration>(config);
}
/// <summary>
/// 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
/// </summary>
private IOcelotConfiguration SetUpConfiguration()
{
var response = _configurationValidator.IsValid(_options.Value);
var reRoutes = new List<ReRoute>();
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<string>();
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<ClaimToHeader>());
}
private List<ClaimToHeader> GetHeadersToAddToRequest(YamlReRoute reRoute)
{
var configHeaders = new List<ClaimToHeader>();
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] == '{';
}
}
}

View File

@ -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
{
/// <summary>
/// Register as singleton
/// </summary>
public class YamlOcelotConfigurationProvider : IOcelotConfigurationProvider
{
private readonly IOcelotConfigurationRepository _repo;
private readonly IOcelotConfigurationCreator _creator;
public YamlOcelotConfigurationProvider(IOcelotConfigurationRepository repo,
IOcelotConfigurationCreator creator)
{
_repo = repo;
_creator = creator;
}
public Response<IOcelotConfiguration> Get()
{
var config = _repo.Get();
if (config.IsError)
{
return new ErrorResponse<IOcelotConfiguration>(config.Errors);
}
if (config.Data == null)
{
var configuration = _creator.Create();
if (configuration.IsError)
{
return new ErrorResponse<IOcelotConfiguration>(configuration.Errors);
}
_repo.AddOrReplace(configuration.Data);
return new OkResponse<IOcelotConfiguration>(configuration.Data);
}
return new OkResponse<IOcelotConfiguration>(config.Data);
}
}
}

View File

@ -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 Authentication;
using Configuration; using Configuration;
@ -16,22 +21,30 @@
public static class ServiceCollectionExtensions 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 // initial configuration from yaml
services.Configure<YamlConfiguration>(configurationRoot); services.Configure<YamlConfiguration>(configurationRoot);
// ocelot services. // ocelot services.
services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>(); services.AddSingleton<IOcelotConfigurationCreator, YamlOcelotConfigurationCreator>();
services.AddSingleton<IClaimsParser, ClaimsParser>(); services.AddSingleton<IOcelotConfigurationProvider, YamlOcelotConfigurationProvider>();
services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
services.AddSingleton<IClaimToHeaderConfigurationParser, ClaimToHeaderConfigurationParser>(); services.AddSingleton<IClaimToHeaderConfigurationParser, ClaimToHeaderConfigurationParser>();
services.AddSingleton<IConfigurationValidator, ConfigurationValidator>(); services.AddSingleton<IConfigurationValidator, ConfigurationValidator>();
services.AddSingleton<IOcelotConfiguration, OcelotConfiguration>();
return services;
}
public static IServiceCollection AddOcelot(this IServiceCollection services)
{
// framework services
services.AddMvcCore().AddJsonFormatters();
services.AddLogging();
// ocelot services.
services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
services.AddSingleton<IClaimsParser, ClaimsParser>();
services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>(); services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
services.AddSingleton<ITemplateVariableNameAndValueFinder, TemplateVariableNameAndValueFinder>(); services.AddSingleton<ITemplateVariableNameAndValueFinder, TemplateVariableNameAndValueFinder>();
services.AddSingleton<IDownstreamUrlTemplateVariableReplacer, DownstreamUrlTemplateVariableReplacer>(); services.AddSingleton<IDownstreamUrlTemplateVariableReplacer, DownstreamUrlTemplateVariableReplacer>();

View File

@ -1,29 +1,31 @@
namespace Ocelot.Library.DownstreamRouteFinder using System;
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Configuration; using Ocelot.Library.Configuration.Provider;
using Errors; using Ocelot.Library.Errors;
using Responses; using Ocelot.Library.Responses;
using UrlMatcher; using Ocelot.Library.UrlMatcher;
namespace Ocelot.Library.DownstreamRouteFinder
{
public class DownstreamRouteFinder : IDownstreamRouteFinder public class DownstreamRouteFinder : IDownstreamRouteFinder
{ {
private readonly IOcelotConfiguration _configuration; private readonly IOcelotConfigurationProvider _configProvider;
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly ITemplateVariableNameAndValueFinder _templateVariableNameAndValueFinder; 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; _urlMatcher = urlMatcher;
_templateVariableNameAndValueFinder = templateVariableNameAndValueFinder; _templateVariableNameAndValueFinder = templateVariableNameAndValueFinder;
} }
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod) public Response<DownstreamRoute> 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); var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplatePattern);

View File

@ -28,7 +28,8 @@ namespace Ocelot
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) 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. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

View File

@ -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<IOcelotConfiguration> _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();
}
/// <summary>
/// long runnnig unit test to make sure repo thread safeok on
/// </summary>
[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<ReRoute> ReRoutes => new List<ReRoute>
{
new ReRouteBuilder().WithDownstreamTemplate(_downstreamTemplate).Build()
};
}
}
}

View File

@ -2,6 +2,9 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using Ocelot.Library.Configuration.Builder;
using Ocelot.Library.Configuration.Parser;
using Ocelot.Library.Configuration.Repository;
using Ocelot.Library.RequestBuilder; using Ocelot.Library.RequestBuilder;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
@ -9,26 +12,28 @@ using Xunit;
namespace Ocelot.UnitTests.Configuration namespace Ocelot.UnitTests.Configuration
{ {
using Library.Builder;
using Library.Configuration; using Library.Configuration;
using Library.Configuration.Yaml; using Library.Configuration.Yaml;
using Library.Responses; using Library.Responses;
public class OcelotConfigurationTests public class YamlConfigurationCreatorTests
{ {
private readonly Mock<IOptions<YamlConfiguration>> _yamlConfig; private readonly Mock<IOptions<YamlConfiguration>> _yamlConfig;
private readonly Mock<IConfigurationValidator> _validator; private readonly Mock<IConfigurationValidator> _validator;
private OcelotConfiguration _config; private Response<IOcelotConfiguration> _config;
private YamlConfiguration _yamlConfiguration; private YamlConfiguration _yamlConfiguration;
private readonly Mock<IClaimToHeaderConfigurationParser> _configExtractor; private readonly Mock<IClaimToHeaderConfigurationParser> _configParser;
private readonly Mock<ILogger<OcelotConfiguration>> _logger; private readonly Mock<ILogger<YamlOcelotConfigurationCreator>> _logger;
private readonly YamlOcelotConfigurationCreator _ocelotConfigurationCreator;
public OcelotConfigurationTests() public YamlConfigurationCreatorTests()
{ {
_logger = new Mock<ILogger<OcelotConfiguration>>(); _logger = new Mock<ILogger<YamlOcelotConfigurationCreator>>();
_configExtractor = new Mock<IClaimToHeaderConfigurationParser>(); _configParser = new Mock<IClaimToHeaderConfigurationParser>();
_validator = new Mock<IConfigurationValidator>(); _validator = new Mock<IConfigurationValidator>();
_yamlConfig = new Mock<IOptions<YamlConfiguration>>(); _yamlConfig = new Mock<IOptions<YamlConfiguration>>();
_ocelotConfigurationCreator = new YamlOcelotConfigurationCreator(
_yamlConfig.Object, _validator.Object, _configParser.Object, _logger.Object);
} }
[Fact] [Fact]
@ -47,7 +52,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid()) .And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRouteBuilder() new ReRouteBuilder()
@ -109,7 +114,7 @@ namespace Ocelot.UnitTests.Configuration
})) }))
.And(x => x.GivenTheYamlConfigIsValid()) .And(x => x.GivenTheYamlConfigIsValid())
.And(x => x.GivenTheConfigHeaderExtractorReturns(new ClaimToHeader("CustomerId", "CustomerId", "", 0))) .And(x => x.GivenTheConfigHeaderExtractorReturns(new ClaimToHeader("CustomerId", "CustomerId", "", 0)))
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected))
.BDDfy(); .BDDfy();
@ -117,7 +122,7 @@ namespace Ocelot.UnitTests.Configuration
private void GivenTheConfigHeaderExtractorReturns(ClaimToHeader expected) private void GivenTheConfigHeaderExtractorReturns(ClaimToHeader expected)
{ {
_configExtractor _configParser
.Setup(x => x.Extract(It.IsAny<string>(), It.IsAny<string>())) .Setup(x => x.Extract(It.IsAny<string>(), It.IsAny<string>()))
.Returns(new OkResponse<ClaimToHeader>(expected)); .Returns(new OkResponse<ClaimToHeader>(expected));
} }
@ -162,7 +167,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid()) .And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected))
.BDDfy(); .BDDfy();
@ -184,7 +189,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid()) .And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRouteBuilder() new ReRouteBuilder()
@ -213,7 +218,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid()) .And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRouteBuilder() new ReRouteBuilder()
@ -242,7 +247,7 @@ namespace Ocelot.UnitTests.Configuration
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid()) .And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRouteBuilder() new ReRouteBuilder()
@ -270,17 +275,16 @@ namespace Ocelot.UnitTests.Configuration
.Returns(_yamlConfiguration); .Returns(_yamlConfiguration);
} }
private void WhenIInstanciateTheOcelotConfig() private void WhenICreateTheConfig()
{ {
_config = new OcelotConfiguration(_yamlConfig.Object, _validator.Object, _config = _ocelotConfigurationCreator.Create();
_configExtractor.Object, _logger.Object);
} }
private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes) private void ThenTheReRoutesAre(List<ReRoute> 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]; var expected = expectedReRoutes[i];
result.DownstreamTemplate.ShouldBe(expected.DownstreamTemplate); result.DownstreamTemplate.ShouldBe(expected.DownstreamTemplate);
@ -292,9 +296,9 @@ namespace Ocelot.UnitTests.Configuration
private void ThenTheAuthenticationOptionsAre(List<ReRoute> expectedReRoutes) private void ThenTheAuthenticationOptionsAre(List<ReRoute> 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; var expected = expectedReRoutes[i].AuthenticationOptions;
result.AdditionalScopes.ShouldBe(expected.AdditionalScopes); result.AdditionalScopes.ShouldBe(expected.AdditionalScopes);

View File

@ -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<IOcelotConfigurationRepository> _configurationRepository;
private readonly Mock<IOcelotConfigurationCreator> _creator;
private Response<IOcelotConfiguration> _result;
public YamlConfigurationProviderTests()
{
_creator = new Mock<IOcelotConfigurationCreator>();
_configurationRepository = new Mock<IOcelotConfigurationRepository>();
_ocelotConfigurationProvider = new YamlOcelotConfigurationProvider(_configurationRepository.Object, _creator.Object);
}
[Fact]
public void should_get_config()
{
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
.When(x => x.WhenIGetTheConfig())
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
.BDDfy();
}
[Fact]
public void should_create_config_if_it_doesnt_exist()
{
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(null)))
.And(x => x.GivenTheCreatorReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
.When(x => x.WhenIGetTheConfig())
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
.BDDfy();
}
[Fact]
public void should_return_error()
{
this.Given(x => x.GivenTheRepoReturns(new ErrorResponse<IOcelotConfiguration>(new List<Error>
{
new AnyError()
})))
.When(x => x.WhenIGetTheConfig())
.Then(x => x.TheFollowingIsReturned(
new ErrorResponse<IOcelotConfiguration>(new List<Error>
{
new AnyError()
})))
.BDDfy();
}
[Fact]
public void should_return_error_if_creator_errors()
{
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(null)))
.And(x => x.GivenTheCreatorReturns(new ErrorResponse<IOcelotConfiguration>(new List<Error>
{
new AnyError()
})))
.When(x => x.WhenIGetTheConfig())
.Then(x => x.TheFollowingIsReturned(new ErrorResponse<IOcelotConfiguration>(new List<Error>
{
new AnyError()
})))
.BDDfy();
}
private void GivenTheCreatorReturns(Response<IOcelotConfiguration> config)
{
_creator
.Setup(x => x.Create())
.Returns(config);
}
private void GivenTheRepoReturns(Response<IOcelotConfiguration> config)
{
_configurationRepository
.Setup(x => x.Get())
.Returns(config);
}
private void WhenIGetTheConfig()
{
_result = _ocelotConfigurationProvider.Get();
}
private void TheFollowingIsReturned(Response<IOcelotConfiguration> expected)
{
_result.IsError.ShouldBe(expected.IsError);
}
class AnyError : Error
{
public AnyError()
: base("blamo", OcelotErrorCode.UnknownError)
{
}
}
}
}

View File

@ -1,12 +1,13 @@
using System.Collections.Generic; using System.Collections.Generic;
using Moq; using Moq;
using Ocelot.Library.Configuration.Builder;
using Ocelot.Library.Configuration.Provider;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.DownstreamRouteFinder namespace Ocelot.UnitTests.DownstreamRouteFinder
{ {
using Library.Builder;
using Library.Configuration; using Library.Configuration;
using Library.DownstreamRouteFinder; using Library.DownstreamRouteFinder;
using Library.Responses; using Library.Responses;
@ -15,7 +16,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
public class DownstreamRouteFinderTests public class DownstreamRouteFinderTests
{ {
private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly Mock<IOcelotConfiguration> _mockConfig; private readonly Mock<IOcelotConfigurationProvider> _mockConfig;
private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher; private readonly Mock<IUrlPathToUrlTemplateMatcher> _mockMatcher;
private readonly Mock<ITemplateVariableNameAndValueFinder> _finder; private readonly Mock<ITemplateVariableNameAndValueFinder> _finder;
private string _upstreamUrlPath; private string _upstreamUrlPath;
@ -26,7 +27,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
public DownstreamRouteFinderTests() public DownstreamRouteFinderTests()
{ {
_mockConfig = new Mock<IOcelotConfiguration>(); _mockConfig = new Mock<IOcelotConfigurationProvider>();
_mockMatcher = new Mock<IUrlPathToUrlTemplateMatcher>(); _mockMatcher = new Mock<IUrlPathToUrlTemplateMatcher>();
_finder = new Mock<ITemplateVariableNameAndValueFinder>(); _finder = new Mock<ITemplateVariableNameAndValueFinder>();
_downstreamRouteFinder = new DownstreamRouteFinder(_mockConfig.Object, _mockMatcher.Object, _finder.Object); _downstreamRouteFinder = new DownstreamRouteFinder(_mockConfig.Object, _mockMatcher.Object, _finder.Object);
@ -157,8 +158,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
{ {
_reRoutesConfig = reRoutesConfig; _reRoutesConfig = reRoutesConfig;
_mockConfig _mockConfig
.Setup(x => x.ReRoutes) .Setup(x => x.Get())
.Returns(_reRoutesConfig); .Returns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(_reRoutesConfig)));
} }
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)

View File

@ -6,13 +6,13 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Library.Configuration.Builder;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Middleware namespace Ocelot.UnitTests.Middleware
{ {
using Library.Authentication; using Library.Authentication;
using Library.Builder;
using Library.DownstreamRouteFinder; using Library.DownstreamRouteFinder;
using Library.Middleware; using Library.Middleware;
using Library.Repository; using Library.Repository;

View File

@ -6,12 +6,12 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Library.Configuration.Builder;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Middleware namespace Ocelot.UnitTests.Middleware
{ {
using Library.Builder;
using Library.DownstreamRouteFinder; using Library.DownstreamRouteFinder;
using Library.Middleware; using Library.Middleware;
using Library.Repository; using Library.Repository;

View File

@ -1,10 +1,11 @@
namespace Ocelot.UnitTests.Middleware using Ocelot.Library.Configuration.Builder;
namespace Ocelot.UnitTests.Middleware
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using Library.Builder;
using Library.DownstreamRouteFinder; using Library.DownstreamRouteFinder;
using Library.Middleware; using Library.Middleware;
using Library.Repository; using Library.Repository;

View File

@ -7,8 +7,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Library.Builder;
using Ocelot.Library.Configuration; using Ocelot.Library.Configuration;
using Ocelot.Library.Configuration.Builder;
using Ocelot.Library.DownstreamRouteFinder; using Ocelot.Library.DownstreamRouteFinder;
using Ocelot.Library.Middleware; using Ocelot.Library.Middleware;
using Ocelot.Library.Repository; using Ocelot.Library.Repository;

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Ocelot.Library.Configuration; using Ocelot.Library.Configuration;
using Ocelot.Library.Configuration.Parser;
using Ocelot.Library.Errors; using Ocelot.Library.Errors;
using Ocelot.Library.RequestBuilder; using Ocelot.Library.RequestBuilder;
using Ocelot.Library.Responses; using Ocelot.Library.Responses;

View File

@ -1,11 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using Ocelot.Library.Configuration.Builder;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.UrlTemplateReplacer namespace Ocelot.UnitTests.UrlTemplateReplacer
{ {
using Library.Builder;
using Library.DownstreamRouteFinder; using Library.DownstreamRouteFinder;
using Library.Responses; using Library.Responses;
using Library.UrlMatcher; using Library.UrlMatcher;