diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index cbe2e910..972adedd 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -33,7 +33,6 @@ namespace Ocelot.Configuration.Builder private List _upstreamHeaderFindAndReplace; private List _downstreamHeaderFindAndReplace; private readonly List _downstreamAddresses; - private string _upstreamHost; private string _key; private List _delegatingHandlers; private List _addHeadersToDownstream; @@ -54,12 +53,6 @@ namespace Ocelot.Configuration.Builder return this; } - public DownstreamReRouteBuilder WithUpstreamHost(string upstreamAddresses) - { - _upstreamHost = upstreamAddresses; - return this; - } - public DownstreamReRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions) { _loadBalancerOptions = loadBalancerOptions; diff --git a/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs index 525f653e..53fc54c7 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs @@ -1,39 +1,46 @@ -namespace Ocelot.Configuration.Builder -{ - public class ReRouteOptionsBuilder - { - private bool _isAuthenticated; - private bool _isAuthorised; - private bool _isCached; - private bool _enableRateLimiting; - - public ReRouteOptionsBuilder WithIsCached(bool isCached) - { - _isCached = isCached; - return this; - } - - public ReRouteOptionsBuilder WithIsAuthenticated(bool isAuthenticated) - { - _isAuthenticated = isAuthenticated; - return this; - } - - public ReRouteOptionsBuilder WithIsAuthorised(bool isAuthorised) - { - _isAuthorised = isAuthorised; - return this; - } - - public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting) - { - _enableRateLimiting = enableRateLimiting; - return this; - } - - public ReRouteOptions Build() - { - return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting); - } - } -} +namespace Ocelot.Configuration.Builder +{ + public class ReRouteOptionsBuilder + { + private bool _isAuthenticated; + private bool _isAuthorised; + private bool _isCached; + private bool _enableRateLimiting; + private bool _useServiceDiscovery; + + public ReRouteOptionsBuilder WithIsCached(bool isCached) + { + _isCached = isCached; + return this; + } + + public ReRouteOptionsBuilder WithIsAuthenticated(bool isAuthenticated) + { + _isAuthenticated = isAuthenticated; + return this; + } + + public ReRouteOptionsBuilder WithIsAuthorised(bool isAuthorised) + { + _isAuthorised = isAuthorised; + return this; + } + + public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting) + { + _enableRateLimiting = enableRateLimiting; + return this; + } + + public ReRouteOptionsBuilder WithUseServiceDiscovery(bool useServiceDiscovery) + { + _useServiceDiscovery = useServiceDiscovery; + return this; + } + + public ReRouteOptions Build() + { + return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting, _useServiceDiscovery); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/AggregatesCreator.cs b/src/Ocelot/Configuration/Creator/AggregatesCreator.cs new file mode 100644 index 00000000..ed43f598 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/AggregatesCreator.cs @@ -0,0 +1,50 @@ +namespace Ocelot.Configuration.Creator +{ + using System.Collections.Generic; + using System.Linq; + using Builder; + using File; + + public class AggregatesCreator : IAggregatesCreator + { + private readonly IUpstreamTemplatePatternCreator _creator; + + public AggregatesCreator(IUpstreamTemplatePatternCreator creator) + { + _creator = creator; + } + + public List Create(FileConfiguration fileConfiguration, List reRoutes) + { + return fileConfiguration.Aggregates + .Select(aggregate => SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration)) + .Where(aggregate => aggregate != null) + .ToList(); + } + + private ReRoute SetUpAggregateReRoute(IEnumerable reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration) + { + var applicableReRoutes = reRoutes + .SelectMany(x => x.DownstreamReRoute) + .Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key)) + .ToList(); + + if (applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count) + { + return null; + } + + var upstreamTemplatePattern = _creator.Create(aggregateReRoute); + + var reRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod) + .WithUpstreamPathTemplate(upstreamTemplatePattern) + .WithDownstreamReRoutes(applicableReRoutes) + .WithUpstreamHost(aggregateReRoute.UpstreamHost) + .WithAggregator(aggregateReRoute.Aggregator) + .Build(); + + return reRoute; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs new file mode 100644 index 00000000..e6fe13ac --- /dev/null +++ b/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs @@ -0,0 +1,55 @@ +namespace Ocelot.Configuration.Creator +{ + using System; + using System.Collections.Generic; + using File; + using DependencyInjection; + using Microsoft.Extensions.DependencyInjection; + + public class ConfigurationCreator : IConfigurationCreator + { + private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator; + private readonly IQoSOptionsCreator _qosOptionsCreator; + private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; + private readonly IAdministrationPath _adminPath; + private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator; + + public ConfigurationCreator( + IServiceProviderConfigurationCreator serviceProviderConfigCreator, + IQoSOptionsCreator qosOptionsCreator, + IHttpHandlerOptionsCreator httpHandlerOptionsCreator, + IServiceProvider serviceProvider, + ILoadBalancerOptionsCreator loadBalancerOptionsCreator + ) + { + _adminPath = serviceProvider.GetService(); + _loadBalancerOptionsCreator = loadBalancerOptionsCreator; + _serviceProviderConfigCreator = serviceProviderConfigCreator; + _qosOptionsCreator = qosOptionsCreator; + _httpHandlerOptionsCreator = httpHandlerOptionsCreator; + } + + public InternalConfiguration Create(FileConfiguration fileConfiguration, List reRoutes) + { + var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration); + + var lbOptions = _loadBalancerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.LoadBalancerOptions); + + var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions); + + var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions); + + var adminPath = _adminPath != null ? _adminPath.Path : null; + + return new InternalConfiguration(reRoutes, + adminPath, + serviceProviderConfiguration, + fileConfiguration.GlobalConfiguration.RequestIdKey, + lbOptions, + fileConfiguration.GlobalConfiguration.DownstreamScheme, + qosOptions, + httpHandlerOptions + ); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/DynamicsCreator.cs b/src/Ocelot/Configuration/Creator/DynamicsCreator.cs new file mode 100644 index 00000000..96d56287 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/DynamicsCreator.cs @@ -0,0 +1,42 @@ +namespace Ocelot.Configuration.Creator +{ + using System.Collections.Generic; + using System.Linq; + using Builder; + using File; + + public class DynamicsCreator : IDynamicsCreator + { + private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; + + public DynamicsCreator(IRateLimitOptionsCreator rateLimitOptionsCreator) + { + _rateLimitOptionsCreator = rateLimitOptionsCreator; + } + + public List Create(FileConfiguration fileConfiguration) + { + return fileConfiguration.DynamicReRoutes + .Select(dynamic => SetUpDynamicReRoute(dynamic, fileConfiguration.GlobalConfiguration)) + .ToList(); + } + + private ReRoute SetUpDynamicReRoute(FileDynamicReRoute fileDynamicReRoute, FileGlobalConfiguration globalConfiguration) + { + var rateLimitOption = _rateLimitOptionsCreator + .Create(fileDynamicReRoute.RateLimitRule, globalConfiguration); + + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithEnableRateLimiting(rateLimitOption.EnableRateLimiting) + .WithRateLimitOptions(rateLimitOption) + .WithServiceName(fileDynamicReRoute.ServiceName) + .Build(); + + var reRoute = new ReRouteBuilder() + .WithDownstreamReRoute(downstreamReRoute) + .Build(); + + return reRoute; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs index 1acbe0fb..f59f8a80 100644 --- a/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs @@ -1,84 +1,35 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Options; -using Ocelot.Cache; -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Validator; -using Ocelot.DependencyInjection; -using Ocelot.Logging; -using Ocelot.Responses; -using Microsoft.Extensions.DependencyInjection; - namespace Ocelot.Configuration.Creator { - using LoadBalancer.LoadBalancers; + using System.Linq; + using System.Threading.Tasks; + using File; + using Validator; + using Responses; - /// - /// Register as singleton - /// public class FileInternalConfigurationCreator : IInternalConfigurationCreator { private readonly IConfigurationValidator _configurationValidator; - private readonly IOcelotLogger _logger; - private readonly IClaimsToThingCreator _claimsToThingCreator; - private readonly IAuthenticationOptionsCreator _authOptionsCreator; - private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator; - private readonly IRequestIdKeyCreator _requestIdKeyCreator; - private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator; - private readonly IQoSOptionsCreator _qosOptionsCreator; - private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator; - private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; - private readonly IRegionCreator _regionCreator; - private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; - private readonly IAdministrationPath _adminPath; - private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator; - private readonly IDownstreamAddressesCreator _downstreamAddressesCreator; + private readonly IConfigurationCreator _configCreator; + private readonly IDynamicsCreator _dynamicsCreator; + private readonly IReRoutesCreator _reRoutesCreator; + private readonly IAggregatesCreator _aggregatesCreator; public FileInternalConfigurationCreator( IConfigurationValidator configurationValidator, - IOcelotLoggerFactory loggerFactory, - IClaimsToThingCreator claimsToThingCreator, - IAuthenticationOptionsCreator authOptionsCreator, - IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator, - IRequestIdKeyCreator requestIdKeyCreator, - IServiceProviderConfigurationCreator serviceProviderConfigCreator, - IQoSOptionsCreator qosOptionsCreator, - IReRouteOptionsCreator fileReRouteOptionsCreator, - IRateLimitOptionsCreator rateLimitOptionsCreator, - IRegionCreator regionCreator, - IHttpHandlerOptionsCreator httpHandlerOptionsCreator, - IServiceProvider serviceProvider, - IHeaderFindAndReplaceCreator headerFAndRCreator, - IDownstreamAddressesCreator downstreamAddressesCreator + IReRoutesCreator reRoutesCreator, + IAggregatesCreator aggregatesCreator, + IDynamicsCreator dynamicsCreator, + IConfigurationCreator configCreator ) { - _downstreamAddressesCreator = downstreamAddressesCreator; - _headerFAndRCreator = headerFAndRCreator; - _adminPath = serviceProvider.GetService(); - _regionCreator = regionCreator; - _rateLimitOptionsCreator = rateLimitOptionsCreator; - _requestIdKeyCreator = requestIdKeyCreator; - _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; - _authOptionsCreator = authOptionsCreator; + _configCreator = configCreator; + _dynamicsCreator = dynamicsCreator; + _aggregatesCreator = aggregatesCreator; + _reRoutesCreator = reRoutesCreator; _configurationValidator = configurationValidator; - _logger = loggerFactory.CreateLogger(); - _claimsToThingCreator = claimsToThingCreator; - _serviceProviderConfigCreator = serviceProviderConfigCreator; - _qosOptionsCreator = qosOptionsCreator; - _fileReRouteOptionsCreator = fileReRouteOptionsCreator; - _httpHandlerOptionsCreator = httpHandlerOptionsCreator; - } - - public async Task> Create(FileConfiguration fileConfiguration) - { - var config = await SetUpConfiguration(fileConfiguration); - return config; } - private async Task> SetUpConfiguration(FileConfiguration fileConfiguration) + public async Task> Create(FileConfiguration fileConfiguration) { var response = await _configurationValidator.IsValid(fileConfiguration); @@ -87,197 +38,20 @@ namespace Ocelot.Configuration.Creator return new ErrorResponse(response.Data.Errors); } - var reRoutes = new List(); + var reRoutes = _reRoutesCreator.Create(fileConfiguration); - foreach (var reRoute in fileConfiguration.ReRoutes) - { - var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration); + var aggregates = _aggregatesCreator.Create(fileConfiguration, reRoutes); - var ocelotReRoute = SetUpReRoute(reRoute, downstreamReRoute); - - reRoutes.Add(ocelotReRoute); - } + var dynamicReRoute = _dynamicsCreator.Create(fileConfiguration); - foreach (var aggregate in fileConfiguration.Aggregates) - { - var ocelotReRoute = SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration); - reRoutes.Add(ocelotReRoute); - } + var mergedReRoutes = reRoutes + .Union(aggregates) + .Union(dynamicReRoute) + .ToList(); - foreach(var fileDynamicReRoute in fileConfiguration.DynamicReRoutes) - { - var reRoute = SetUpDynamicReRoute(fileDynamicReRoute, fileConfiguration.GlobalConfiguration); - reRoutes.Add(reRoute); - } - - var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration); - - var lbOptions = CreateLoadBalancerOptions(fileConfiguration.GlobalConfiguration.LoadBalancerOptions); - - var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions); - - var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions); - - var adminPath = _adminPath != null ? _adminPath.Path : null; - - var config = new InternalConfiguration(reRoutes, - adminPath, - serviceProviderConfiguration, - fileConfiguration.GlobalConfiguration.RequestIdKey, - lbOptions, - fileConfiguration.GlobalConfiguration.DownstreamScheme, - qosOptions, - httpHandlerOptions - ); + var config = _configCreator.Create(fileConfiguration, mergedReRoutes); return new OkResponse(config); } - - private ReRoute SetUpDynamicReRoute(FileDynamicReRoute fileDynamicReRoute, FileGlobalConfiguration globalConfiguration) - { - var rateLimitOption = _rateLimitOptionsCreator.Create(fileDynamicReRoute.RateLimitRule, globalConfiguration); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithEnableRateLimiting(true) - .WithRateLimitOptions(rateLimitOption) - .WithServiceName(fileDynamicReRoute.ServiceName) - .Build(); - - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .Build(); - - return reRoute; - } - - private ReRoute SetUpAggregateReRoute(List reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration) - { - var applicableReRoutes = reRoutes - .SelectMany(x => x.DownstreamReRoute) - .Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key)) - .ToList(); - - if(applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count) - { - //todo - log or throw or return error whatever? - } - - //make another re route out of these - var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(aggregateReRoute); - - var reRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod) - .WithUpstreamPathTemplate(upstreamTemplatePattern) - .WithDownstreamReRoutes(applicableReRoutes) - .WithUpstreamHost(aggregateReRoute.UpstreamHost) - .WithAggregator(aggregateReRoute.Aggregator) - .Build(); - - return reRoute; - } - - private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes) - { - var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); - - var reRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) - .WithUpstreamPathTemplate(upstreamTemplatePattern) - .WithDownstreamReRoute(downstreamReRoutes) - .WithUpstreamHost(fileReRoute.UpstreamHost) - .Build(); - - return reRoute; - } - - private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) - { - var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute); - - var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration); - - var reRouteKey = CreateReRouteKey(fileReRoute); - - var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); - - var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute); - - var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest); - - var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest); - - var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); - - var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod.ToArray()); - - var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute.RateLimitOptions, globalConfiguration); - - var region = _regionCreator.Create(fileReRoute); - - var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions); - - var hAndRs = _headerFAndRCreator.Create(fileReRoute); - - var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute); - - var lbOptions = CreateLoadBalancerOptions(fileReRoute.LoadBalancerOptions); - - var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName); - - var reRoute = new DownstreamReRouteBuilder() - .WithKey(fileReRoute.Key) - .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) - .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) - .WithUpstreamPathTemplate(upstreamTemplatePattern) - .WithIsAuthenticated(fileReRouteOptions.IsAuthenticated) - .WithAuthenticationOptions(authOptionsForRoute) - .WithClaimsToHeaders(claimsToHeaders) - .WithClaimsToClaims(claimsToClaims) - .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) - .WithIsAuthorised(fileReRouteOptions.IsAuthorised) - .WithClaimsToQueries(claimsToQueries) - .WithRequestIdKey(requestIdKey) - .WithIsCached(fileReRouteOptions.IsCached) - .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region)) - .WithDownstreamScheme(fileReRoute.DownstreamScheme) - .WithLoadBalancerOptions(lbOptions) - .WithDownstreamAddresses(downstreamAddresses) - .WithLoadBalancerKey(reRouteKey) - .WithQosOptions(qosOptions) - .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) - .WithRateLimitOptions(rateLimitOption) - .WithHttpHandlerOptions(httpHandlerOptions) - .WithServiceName(fileReRoute.ServiceName) - .WithUseServiceDiscovery(useServiceDiscovery) - .WithUpstreamHeaderFindAndReplace(hAndRs.Upstream) - .WithDownstreamHeaderFindAndReplace(hAndRs.Downstream) - .WithUpstreamHost(fileReRoute.UpstreamHost) - .WithDelegatingHandlers(fileReRoute.DelegatingHandlers) - .WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream) - .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) - .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator) - .Build(); - - return reRoute; - } - - private LoadBalancerOptions CreateLoadBalancerOptions(FileLoadBalancerOptions options) - { - return new LoadBalancerOptionsBuilder() - .WithType(options.Type) - .WithKey(options.Key) - .WithExpiryInMs(options.Expiry) - .Build(); - } - - private string CreateReRouteKey(FileReRoute fileReRoute) - { - if (!string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Type) && !string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Key) && fileReRoute.LoadBalancerOptions.Type == nameof(CookieStickySessions)) - { - return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}"; - } - - return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}|{string.Join(",", fileReRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}"; - } } } diff --git a/src/Ocelot/Configuration/Creator/IAggregatesCreator.cs b/src/Ocelot/Configuration/Creator/IAggregatesCreator.cs new file mode 100644 index 00000000..1586a2be --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IAggregatesCreator.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IAggregatesCreator + { + List Create(FileConfiguration fileConfiguration, List reRoutes); + } +} diff --git a/src/Ocelot/Configuration/Creator/IConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/IConfigurationCreator.cs new file mode 100644 index 00000000..aed2db54 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IConfigurationCreator.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IConfigurationCreator + { + InternalConfiguration Create(FileConfiguration fileConfiguration, List reRoutes); + } +} diff --git a/src/Ocelot/Configuration/Creator/IDynamicsCreator.cs b/src/Ocelot/Configuration/Creator/IDynamicsCreator.cs new file mode 100644 index 00000000..f6c7e16c --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IDynamicsCreator.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IDynamicsCreator + { + List Create(FileConfiguration fileConfiguration); + } +} diff --git a/src/Ocelot/Configuration/Creator/ILoadBalancerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/ILoadBalancerOptionsCreator.cs new file mode 100644 index 00000000..387a0ca7 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/ILoadBalancerOptionsCreator.cs @@ -0,0 +1,9 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface ILoadBalancerOptionsCreator + { + LoadBalancerOptions Create(FileLoadBalancerOptions options); + } +} diff --git a/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs index 3ad42d9f..77ada9c6 100644 --- a/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs @@ -1,11 +1,12 @@ -using Ocelot.Configuration.File; - -namespace Ocelot.Configuration.Creator -{ - public interface IQoSOptionsCreator - { - QoSOptions Create(FileQoSOptions options); - QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods); - QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods); - } -} +using System.Collections.Generic; +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IQoSOptionsCreator + { + QoSOptions Create(FileQoSOptions options); + QoSOptions Create(FileQoSOptions options, string pathTemplate, List httpMethods); + QoSOptions Create(QoSOptions options, string pathTemplate, List httpMethods); + } +} diff --git a/src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs b/src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs new file mode 100644 index 00000000..05f78354 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs @@ -0,0 +1,9 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IReRouteKeyCreator + { + string Create(FileReRoute fileReRoute); + } +} diff --git a/src/Ocelot/Configuration/Creator/IReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/IReRoutesCreator.cs new file mode 100644 index 00000000..608e2af5 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IReRoutesCreator.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IReRoutesCreator + { + List Create(FileConfiguration fileConfiguration); + } +} diff --git a/src/Ocelot/Configuration/Creator/LoadBalancerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/LoadBalancerOptionsCreator.cs new file mode 100644 index 00000000..c7770c24 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/LoadBalancerOptionsCreator.cs @@ -0,0 +1,17 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + + public class LoadBalancerOptionsCreator : ILoadBalancerOptionsCreator + { + public LoadBalancerOptions Create(FileLoadBalancerOptions options) + { + return new LoadBalancerOptionsBuilder() + .WithType(options.Type) + .WithKey(options.Key) + .WithExpiryInMs(options.Expiry) + .Build(); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs b/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs index 6d0af409..27ae2544 100644 --- a/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs @@ -1,47 +1,48 @@ -namespace Ocelot.Configuration.Creator -{ - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.File; - using System.Linq; - - public class QoSOptionsCreator : IQoSOptionsCreator - { - public QoSOptions Create(FileQoSOptions options) - { - return new QoSOptionsBuilder() - .WithExceptionsAllowedBeforeBreaking(options.ExceptionsAllowedBeforeBreaking) - .WithDurationOfBreak(options.DurationOfBreak) - .WithTimeoutValue(options.TimeoutValue) - .Build(); - } - - public QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods) - { - var key = CreateKey(pathTemplate, httpMethods); - - return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking); - } - - public QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods) - { - var key = CreateKey(pathTemplate, httpMethods); - - return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking); - } - - private QoSOptions Map(string key, int timeoutValue, int durationOfBreak, int exceptionsAllowedBeforeBreaking) - { - return new QoSOptionsBuilder() - .WithExceptionsAllowedBeforeBreaking(exceptionsAllowedBeforeBreaking) - .WithDurationOfBreak(durationOfBreak) - .WithTimeoutValue(timeoutValue) - .WithKey(key) - .Build(); - } - - private string CreateKey(string pathTemplate, string[] httpMethods) - { - return $"{pathTemplate.FirstOrDefault()}|{string.Join(",", httpMethods)}"; - } - } -} +namespace Ocelot.Configuration.Creator +{ + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.File; + using System.Collections.Generic; + using System.Linq; + + public class QoSOptionsCreator : IQoSOptionsCreator + { + public QoSOptions Create(FileQoSOptions options) + { + return new QoSOptionsBuilder() + .WithExceptionsAllowedBeforeBreaking(options.ExceptionsAllowedBeforeBreaking) + .WithDurationOfBreak(options.DurationOfBreak) + .WithTimeoutValue(options.TimeoutValue) + .Build(); + } + + public QoSOptions Create(FileQoSOptions options, string pathTemplate, List httpMethods) + { + var key = CreateKey(pathTemplate, httpMethods); + + return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking); + } + + public QoSOptions Create(QoSOptions options, string pathTemplate, List httpMethods) + { + var key = CreateKey(pathTemplate, httpMethods); + + return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking); + } + + private QoSOptions Map(string key, int timeoutValue, int durationOfBreak, int exceptionsAllowedBeforeBreaking) + { + return new QoSOptionsBuilder() + .WithExceptionsAllowedBeforeBreaking(exceptionsAllowedBeforeBreaking) + .WithDurationOfBreak(durationOfBreak) + .WithTimeoutValue(timeoutValue) + .WithKey(key) + .Build(); + } + + private string CreateKey(string pathTemplate, List httpMethods) + { + return $"{pathTemplate.FirstOrDefault()}|{string.Join(",", httpMethods)}"; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs b/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs index 10e63f6f..8f300dd2 100644 --- a/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/RateLimitOptionsCreator.cs @@ -1,5 +1,4 @@ -using System; -using Ocelot.Configuration.Builder; +using Ocelot.Configuration.Builder; using Ocelot.Configuration.File; namespace Ocelot.Configuration.Creator @@ -8,11 +7,9 @@ namespace Ocelot.Configuration.Creator { public RateLimitOptions Create(FileRateLimitRule fileRateLimitRule, FileGlobalConfiguration globalConfiguration) { - RateLimitOptions rateLimitOption = null; - if (fileRateLimitRule != null && fileRateLimitRule.EnableRateLimiting) { - rateLimitOption = new RateLimitOptionsBuilder() + return new RateLimitOptionsBuilder() .WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader) .WithClientWhiteList(fileRateLimitRule.ClientWhitelist) .WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders) @@ -26,7 +23,7 @@ namespace Ocelot.Configuration.Creator .Build(); } - return rateLimitOption; + return new RateLimitOptionsBuilder().WithEnableRateLimiting(false).Build(); } } } diff --git a/src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs b/src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs new file mode 100644 index 00000000..4c7d276d --- /dev/null +++ b/src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs @@ -0,0 +1,31 @@ +using System.Linq; +using Ocelot.Configuration.File; +using Ocelot.LoadBalancer.LoadBalancers; + +namespace Ocelot.Configuration.Creator +{ + public class ReRouteKeyCreator : IReRouteKeyCreator + { + public string Create(FileReRoute fileReRoute) + { + if (IsStickySession(fileReRoute)) + { + return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}"; + } + + return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}|{string.Join(",", fileReRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}"; + } + + private bool IsStickySession(FileReRoute fileReRoute) + { + if (!string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Type) + && !string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Key) + && fileReRoute.LoadBalancerOptions.Type == nameof(CookieStickySessions)) + { + return true; + } + + return false; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs index 3a8d1811..1eb4e372 100644 --- a/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs @@ -11,12 +11,14 @@ namespace Ocelot.Configuration.Creator var isAuthorised = IsAuthorised(fileReRoute); var isCached = IsCached(fileReRoute); var enableRateLimiting = IsEnableRateLimiting(fileReRoute); + var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName); var options = new ReRouteOptionsBuilder() .WithIsAuthenticated(isAuthenticated) .WithIsAuthorised(isAuthorised) .WithIsCached(isCached) .WithRateLimiting(enableRateLimiting) + .WithUseServiceDiscovery(useServiceDiscovery) .Build(); return options; diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs new file mode 100644 index 00000000..d8e5b66d --- /dev/null +++ b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs @@ -0,0 +1,150 @@ +namespace Ocelot.Configuration.Creator +{ + using System.Collections.Generic; + using System.Linq; + using Cache; + using Builder; + using File; + + public class ReRoutesCreator : IReRoutesCreator + { + private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator; + private readonly IClaimsToThingCreator _claimsToThingCreator; + private readonly IAuthenticationOptionsCreator _authOptionsCreator; + private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator; + private readonly IRequestIdKeyCreator _requestIdKeyCreator; + private readonly IQoSOptionsCreator _qosOptionsCreator; + private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator; + private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; + private readonly IRegionCreator _regionCreator; + private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; + private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator; + private readonly IDownstreamAddressesCreator _downstreamAddressesCreator; + private readonly IReRouteKeyCreator _reRouteKeyCreator; + + public ReRoutesCreator( + IClaimsToThingCreator claimsToThingCreator, + IAuthenticationOptionsCreator authOptionsCreator, + IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator, + IRequestIdKeyCreator requestIdKeyCreator, + IQoSOptionsCreator qosOptionsCreator, + IReRouteOptionsCreator fileReRouteOptionsCreator, + IRateLimitOptionsCreator rateLimitOptionsCreator, + IRegionCreator regionCreator, + IHttpHandlerOptionsCreator httpHandlerOptionsCreator, + IHeaderFindAndReplaceCreator headerFAndRCreator, + IDownstreamAddressesCreator downstreamAddressesCreator, + ILoadBalancerOptionsCreator loadBalancerOptionsCreator, + IReRouteKeyCreator reRouteKeyCreator + ) + { + _reRouteKeyCreator = reRouteKeyCreator; + _loadBalancerOptionsCreator = loadBalancerOptionsCreator; + _downstreamAddressesCreator = downstreamAddressesCreator; + _headerFAndRCreator = headerFAndRCreator; + _regionCreator = regionCreator; + _rateLimitOptionsCreator = rateLimitOptionsCreator; + _requestIdKeyCreator = requestIdKeyCreator; + _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; + _authOptionsCreator = authOptionsCreator; + _claimsToThingCreator = claimsToThingCreator; + _qosOptionsCreator = qosOptionsCreator; + _fileReRouteOptionsCreator = fileReRouteOptionsCreator; + _httpHandlerOptionsCreator = httpHandlerOptionsCreator; + _loadBalancerOptionsCreator = loadBalancerOptionsCreator; + } + + public List Create(FileConfiguration fileConfiguration) + { + return fileConfiguration.ReRoutes + .Select(reRoute => + { + var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration); + return SetUpReRoute(reRoute, downstreamReRoute); + }) + .ToList(); + } + + private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) + { + var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute); + + var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration); + + var reRouteKey = _reRouteKeyCreator.Create(fileReRoute); + + var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); + + var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute); + + var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest); + + var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest); + + var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); + + var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod); + + var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute.RateLimitOptions, globalConfiguration); + + var region = _regionCreator.Create(fileReRoute); + + var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions); + + var hAndRs = _headerFAndRCreator.Create(fileReRoute); + + var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute); + + var lbOptions = _loadBalancerOptionsCreator.Create(fileReRoute.LoadBalancerOptions); + + var reRoute = new DownstreamReRouteBuilder() + .WithKey(fileReRoute.Key) + .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) + .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) + .WithUpstreamPathTemplate(upstreamTemplatePattern) + .WithIsAuthenticated(fileReRouteOptions.IsAuthenticated) + .WithAuthenticationOptions(authOptionsForRoute) + .WithClaimsToHeaders(claimsToHeaders) + .WithClaimsToClaims(claimsToClaims) + .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) + .WithIsAuthorised(fileReRouteOptions.IsAuthorised) + .WithClaimsToQueries(claimsToQueries) + .WithRequestIdKey(requestIdKey) + .WithIsCached(fileReRouteOptions.IsCached) + .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region)) + .WithDownstreamScheme(fileReRoute.DownstreamScheme) + .WithLoadBalancerOptions(lbOptions) + .WithDownstreamAddresses(downstreamAddresses) + .WithLoadBalancerKey(reRouteKey) + .WithQosOptions(qosOptions) + .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) + .WithRateLimitOptions(rateLimitOption) + .WithHttpHandlerOptions(httpHandlerOptions) + .WithServiceName(fileReRoute.ServiceName) + .WithUseServiceDiscovery(fileReRouteOptions.UseServiceDiscovery) + .WithUpstreamHeaderFindAndReplace(hAndRs.Upstream) + .WithDownstreamHeaderFindAndReplace(hAndRs.Downstream) + .WithDelegatingHandlers(fileReRoute.DelegatingHandlers) + .WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream) + .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) + .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator) + .Build(); + + return reRoute; + } + + private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes) + { + var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); + + var reRoute = new ReRouteBuilder() + .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) + .WithUpstreamPathTemplate(upstreamTemplatePattern) + .WithDownstreamReRoute(downstreamReRoutes) + .WithUpstreamHost(fileReRoute.UpstreamHost) + .Build(); + + return reRoute; + } + } +} diff --git a/src/Ocelot/Configuration/ReRouteOptions.cs b/src/Ocelot/Configuration/ReRouteOptions.cs index 074e45b6..e2382d5c 100644 --- a/src/Ocelot/Configuration/ReRouteOptions.cs +++ b/src/Ocelot/Configuration/ReRouteOptions.cs @@ -2,17 +2,19 @@ namespace Ocelot.Configuration { public class ReRouteOptions { - public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting) + public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery) { IsAuthenticated = isAuthenticated; IsAuthorised = isAuthorised; IsCached = isCached; EnableRateLimiting = isEnableRateLimiting; + UseServiceDiscovery = useServiceDiscovery; } public bool IsAuthenticated { get; private set; } public bool IsAuthorised { get; private set; } public bool IsCached { get; private set; } public bool EnableRateLimiting { get; private set; } + public bool UseServiceDiscovery { get; private set; } } } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index d2f017c1..77f29c5a 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -56,6 +56,12 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); Services.TryAddSingleton(); Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs index f2c8628b..b032555d 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs @@ -42,7 +42,7 @@ return downstreamRoute; } - var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new []{ upstreamHttpMethod }); + var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new List { upstreamHttpMethod }); var upstreamPathTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue(upstreamUrlPath).Build(); diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs index f28d6bc7..7f3bc902 100644 --- a/test/Ocelot.AcceptanceTests/AggregateTests.cs +++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs @@ -1,446 +1,565 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Ocelot.Configuration.File; -using Ocelot.Middleware; -using Ocelot.Middleware.Multiplexer; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.AcceptanceTests -{ - public class AggregateTests : IDisposable - { - private readonly Steps _steps; - private string _downstreamPathOne; - private string _downstreamPathTwo; - private readonly ServiceHandler _serviceHandler; - - public AggregateTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_with_simple_url_user_defined_aggregate() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51885, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51886, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - }, - Aggregator = "FakeDefinedAggregator" - } - } - }; - - var expected = "Bye from Laura, Bye from Tom"; - - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithSpecficAggregatorsRegisteredInDi()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) - .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_simple_url() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51875, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51886, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}"; - - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51875", "/", 200, "{Hello from Laura}")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) - .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_simple_url_one_service_404() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51881, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51882, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - var expected = "{\"Laura\":,\"Tom\":{Hello from Tom}}"; - - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51881", "/", 404, "")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51882", "/", 200, "{Hello from Tom}")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) - .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_simple_url_both_service_404() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51883, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51884, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - var expected = "{\"Laura\":,\"Tom\":}"; - - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51883", "/", 404, "")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51884", "/", 404, "")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) - .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) - .BDDfy(); - } - - [Fact] - public void should_be_thread_safe() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51880, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51878", "/", 200, "{Hello from Laura}")) - .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51880", "/", 200, "{Hello from Tom}")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIMakeLotsOfDifferentRequestsToTheApiGateway()) - .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) - .BDDfy(); - } - - private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; - - if (_downstreamPathOne != basePath) - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync("downstream path didnt match base path"); - } - else - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - } - }); - } - - private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; - - if (_downstreamPathTwo != basePath) - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync("downstream path didnt match base path"); - } - else - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - } - }); - } - - internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath) - { - _downstreamPathOne.ShouldBe(expectedDownstreamPathOne); - _downstreamPathTwo.ShouldBe(expectedDownstreamPath); - } - - public void Dispose() - { - _serviceHandler.Dispose(); - _steps.Dispose(); - } - } - - public class FakeDepdendency - { - } - - public class FakeDefinedAggregator : IDefinedAggregator - { - private readonly FakeDepdendency _dep; - - public FakeDefinedAggregator(FakeDepdendency dep) - { - _dep = dep; - } - - public async Task Aggregate(List responses) - { - var one = await responses[0].Content.ReadAsStringAsync(); - var two = await responses[1].Content.ReadAsStringAsync(); - - var merge = $"{one}, {two}"; - merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", ""); - var headers = responses.SelectMany(x => x.Headers).ToList(); - return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers); - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Ocelot.Configuration.File; +using Ocelot.Middleware; +using Ocelot.Middleware.Multiplexer; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + public class AggregateTests : IDisposable + { + private readonly Steps _steps; + private string _downstreamPathOne; + private string _downstreamPathTwo; + private readonly ServiceHandler _serviceHandler; + + public AggregateTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_fix_issue_597() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/api/values?MailId={userid}", + UpstreamPathTemplate = "/key1data/{userid}", + UpstreamHttpMethod = new List {"Get"}, + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 8571 + } + }, + Key = "key1" + }, + new FileReRoute + { + DownstreamPathTemplate = "/api/values?MailId={userid}", + UpstreamPathTemplate = "/key2data/{userid}", + UpstreamHttpMethod = new List {"Get"}, + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 8571 + } + }, + Key = "key2" + }, + new FileReRoute + { + DownstreamPathTemplate = "/api/values?MailId={userid}", + UpstreamPathTemplate = "/key3data/{userid}", + UpstreamHttpMethod = new List {"Get"}, + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 8571 + } + }, + Key = "key3" + }, + new FileReRoute + { + DownstreamPathTemplate = "/api/values?MailId={userid}", + UpstreamPathTemplate = "/key4data/{userid}", + UpstreamHttpMethod = new List {"Get"}, + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 8571 + } + }, + Key = "key4" + }, + }, + Aggregates = new List + { + new FileAggregateReRoute + { + ReRouteKeys = new List{ + "key1", + "key2", + "key3", + "key4" + }, + UpstreamPathTemplate = "/EmpDetail/IN/{userid}" + }, + new FileAggregateReRoute + { + ReRouteKeys = new List{ + "key1", + "key2", + }, + UpstreamPathTemplate = "/EmpDetail/US/{userid}" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + RequestIdKey = "CorrelationID" + } + }; + + var expected = "{\"key1\":some_data,\"key2\":some_data}"; + + this.Given(x => x.GivenServiceIsRunning("http://localhost:8571", 200, "some_data")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EmpDetail/US/1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url_user_defined_aggregate() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51885, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51886, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + }, + Aggregator = "FakeDefinedAggregator" + } + } + }; + + var expected = "Bye from Laura, Bye from Tom"; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithSpecficAggregatorsRegisteredInDi()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51875, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51886, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}"; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51875", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url_one_service_404() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51881, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51882, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + var expected = "{\"Laura\":,\"Tom\":{Hello from Tom}}"; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51881", "/", 404, "")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51882", "/", 200, "{Hello from Tom}")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url_both_service_404() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51883, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51884, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + var expected = "{\"Laura\":,\"Tom\":}"; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51883", "/", 404, "")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51884", "/", 404, "")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe(expected)) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + [Fact] + public void should_be_thread_safe() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateReRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + ReRouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51878", "/", 200, "{Hello from Laura}")) + .Given(x => x.GivenServiceTwoIsRunning("http://localhost:51880", "/", 200, "{Hello from Tom}")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIMakeLotsOfDifferentRequestsToTheApiGateway()) + .And(x => ThenTheDownstreamUrlPathShouldBe("/", "/")) + .BDDfy(); + } + + private void GivenServiceIsRunning(string baseUrl, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + } + + private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; + + if (_downstreamPathOne != basePath) + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync("downstream path didnt match base path"); + } + else + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + } + }); + } + + private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; + + if (_downstreamPathTwo != basePath) + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync("downstream path didnt match base path"); + } + else + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + } + }); + } + + internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath) + { + _downstreamPathOne.ShouldBe(expectedDownstreamPathOne); + _downstreamPathTwo.ShouldBe(expectedDownstreamPath); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + } + } + + public class FakeDepdendency + { + } + + public class FakeDefinedAggregator : IDefinedAggregator + { + private readonly FakeDepdendency _dep; + + public FakeDefinedAggregator(FakeDepdendency dep) + { + _dep = dep; + } + + public async Task Aggregate(List responses) + { + var one = await responses[0].Content.ReadAsStringAsync(); + var two = await responses[1].Content.ReadAsStringAsync(); + + var merge = $"{one}, {two}"; + merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", ""); + var headers = responses.SelectMany(x => x.Headers).ToList(); + return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs new file mode 100644 index 00000000..e124fe74 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs @@ -0,0 +1,164 @@ +namespace Ocelot.UnitTests.Configuration +{ + using System.Collections.Generic; + using System.Net.Http; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Values; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class AggregatesCreatorTests + { + private readonly AggregatesCreator _creator; + private readonly Mock _utpCreator; + private FileConfiguration _fileConfiguration; + private List _reRoutes; + private List _result; + private UpstreamPathTemplate _aggregate1Utp; + private UpstreamPathTemplate _aggregate2Utp; + + public AggregatesCreatorTests() + { + _utpCreator = new Mock(); + _creator = new AggregatesCreator(_utpCreator.Object); + } + + [Fact] + public void should_return_no_aggregates() + { + var fileConfig = new FileConfiguration + { + Aggregates = new List + { + new FileAggregateReRoute + { + ReRouteKeys = new List{"key1"} + } + } + }; + var reRoutes = new List(); + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenThe(reRoutes)) + .When(_ => WhenICreate()) + .Then(_ => TheUtpCreatorIsNotCalled()) + .And(_ => ThenTheResultIsNotNull()) + .And(_ => ThenTheResultIsEmpty()) + .BDDfy(); + } + + [Fact] + public void should_create_aggregates() + { + var fileConfig = new FileConfiguration + { + Aggregates = new List + { + new FileAggregateReRoute + { + ReRouteKeys = new List{"key1", "key2"}, + UpstreamHost = "hosty", + UpstreamPathTemplate = "templatey", + Aggregator = "aggregatory", + ReRouteIsCaseSensitive = true + }, + new FileAggregateReRoute + { + ReRouteKeys = new List{"key3", "key4"}, + UpstreamHost = "hosty", + UpstreamPathTemplate = "templatey", + Aggregator = "aggregatory", + ReRouteIsCaseSensitive = true + } + } + }; + + var reRoutes = new List + { + new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key1").Build()).Build(), + new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key2").Build()).Build(), + new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key3").Build()).Build(), + new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key4").Build()).Build() + }; + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenThe(reRoutes)) + .And(_ => GivenTheUtpCreatorReturns()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheUtpCreatorIsCalledCorrectly()) + .And(_ => ThenTheAggregatesAreCreated()) + .BDDfy(); + } + + private void ThenTheAggregatesAreCreated() + { + _result.ShouldNotBeNull(); + _result.Count.ShouldBe(2); + + _result[0].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get); + _result[0].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[0].UpstreamHost); + _result[0].UpstreamTemplatePattern.ShouldBe(_aggregate1Utp); + _result[0].Aggregator.ShouldBe(_fileConfiguration.Aggregates[0].Aggregator); + _result[0].DownstreamReRoute.ShouldContain(x => x == _reRoutes[0].DownstreamReRoute[0]); + _result[0].DownstreamReRoute.ShouldContain(x => x == _reRoutes[1].DownstreamReRoute[0]); + + _result[1].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get); + _result[1].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[1].UpstreamHost); + _result[1].UpstreamTemplatePattern.ShouldBe(_aggregate2Utp); + _result[1].Aggregator.ShouldBe(_fileConfiguration.Aggregates[1].Aggregator); + _result[1].DownstreamReRoute.ShouldContain(x => x == _reRoutes[2].DownstreamReRoute[0]); + _result[1].DownstreamReRoute.ShouldContain(x => x == _reRoutes[3].DownstreamReRoute[0]); + } + + private void ThenTheUtpCreatorIsCalledCorrectly() + { + _utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[0]), Times.Once); + _utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[1]), Times.Once); + } + + private void GivenTheUtpCreatorReturns() + { + _aggregate1Utp = new UpstreamPathTemplateBuilder().Build(); + _aggregate2Utp = new UpstreamPathTemplateBuilder().Build(); + + _utpCreator.SetupSequence(x => x.Create(It.IsAny())) + .Returns(_aggregate1Utp) + .Returns(_aggregate2Utp); + } + + private void ThenTheResultIsEmpty() + { + _result.Count.ShouldBe(0); + } + + private void ThenTheResultIsNotNull() + { + _result.ShouldNotBeNull(); + } + + private void TheUtpCreatorIsNotCalled() + { + _utpCreator.Verify(x => x.Create(It.IsAny()), Times.Never); + } + + private void GivenThe(FileConfiguration fileConfiguration) + { + _fileConfiguration = fileConfiguration; + } + + private void GivenThe(List reRoutes) + { + _reRoutes = reRoutes; + } + + private void WhenICreate() + { + _result = _creator.Create(_fileConfiguration, _reRoutes); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs new file mode 100644 index 00000000..1e288265 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs @@ -0,0 +1,124 @@ +namespace Ocelot.UnitTests.Configuration +{ + using System.Collections.Generic; + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Ocelot.DependencyInjection; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class ConfigurationCreatorTests + { + private ConfigurationCreator _creator; + private InternalConfiguration _result; + private readonly Mock _spcCreator; + private readonly Mock _qosCreator; + private readonly Mock _hhoCreator; + private readonly Mock _lboCreator; + private FileConfiguration _fileConfig; + private List _reRoutes; + private ServiceProviderConfiguration _spc; + private LoadBalancerOptions _lbo; + private QoSOptions _qoso; + private HttpHandlerOptions _hho; + private AdministrationPath _adminPath; + private readonly ServiceCollection _serviceCollection; + + public ConfigurationCreatorTests() + { + _lboCreator = new Mock(); + _hhoCreator = new Mock(); + _qosCreator = new Mock(); + _spcCreator = new Mock(); + _serviceCollection = new ServiceCollection(); + } + + [Fact] + public void should_build_configuration_with_no_admin_path() + { + this.Given(_ => GivenTheDependenciesAreSetUp()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDepdenciesAreCalledCorrectly()) + .And(_ => ThenThePropertiesAreSetCorrectly()) + .And(_ => ThenTheAdminPathIsNull()) + .BDDfy(); + } + + [Fact] + public void should_build_configuration_with_admin_path() + { + this.Given(_ => GivenTheDependenciesAreSetUp()) + .And(_ => GivenTheAdminPath()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDepdenciesAreCalledCorrectly()) + .And(_ => ThenThePropertiesAreSetCorrectly()) + .And(_ => ThenTheAdminPathIsSet()) + .BDDfy(); + } + + private void ThenTheAdminPathIsNull() + { + _result.AdministrationPath.ShouldBeNull(); + } + + private void ThenThePropertiesAreSetCorrectly() + { + _result.ShouldNotBeNull(); + _result.ServiceProviderConfiguration.ShouldBe(_spc); + _result.LoadBalancerOptions.ShouldBe(_lbo); + _result.QoSOptions.ShouldBe(_qoso); + _result.HttpHandlerOptions.ShouldBe(_hho); + _result.ReRoutes.ShouldBe(_reRoutes); + _result.RequestId.ShouldBe(_fileConfig.GlobalConfiguration.RequestIdKey); + _result.DownstreamScheme.ShouldBe(_fileConfig.GlobalConfiguration.DownstreamScheme); + } + + private void ThenTheAdminPathIsSet() + { + _result.AdministrationPath.ShouldBe("wooty"); + } + + private void ThenTheDepdenciesAreCalledCorrectly() + { + _spcCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration), Times.Once); + _lboCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.LoadBalancerOptions), Times.Once); + _qosCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.QoSOptions), Times.Once); + _hhoCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.HttpHandlerOptions), Times.Once); + } + + private void GivenTheAdminPath() + { + _adminPath = new AdministrationPath("wooty"); + _serviceCollection.AddSingleton(_adminPath); + } + + private void GivenTheDependenciesAreSetUp() + { + _fileConfig = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + }; + _reRoutes = new List(); + _spc = new ServiceProviderConfiguration("", "", 1, "", "", 1); + _lbo = new LoadBalancerOptionsBuilder().Build(); + _qoso = new QoSOptions(1, 1, 1, ""); + _hho = new HttpHandlerOptionsBuilder().Build(); + + _spcCreator.Setup(x => x.Create(It.IsAny())).Returns(_spc); + _lboCreator.Setup(x => x.Create(It.IsAny())).Returns(_lbo); + _qosCreator.Setup(x => x.Create(It.IsAny())).Returns(_qoso); + _hhoCreator.Setup(x => x.Create(It.IsAny())).Returns(_hho); + } + + private void WhenICreate() + { + var serviceProvider = _serviceCollection.BuildServiceProvider(); + _creator = new ConfigurationCreator(_spcCreator.Object, _qosCreator.Object, _hhoCreator.Object, serviceProvider, _lboCreator.Object); + _result = _creator.Create(_fileConfig, _reRoutes); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs new file mode 100644 index 00000000..6c6beb9c --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs @@ -0,0 +1,126 @@ +namespace Ocelot.UnitTests.Configuration +{ + using System.Collections.Generic; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class DynamicsCreatorTests + { + private readonly DynamicsCreator _creator; + private readonly Mock _rloCreator; + private List _result; + private FileConfiguration _fileConfig; + private RateLimitOptions _rlo1; + private RateLimitOptions _rlo2; + + public DynamicsCreatorTests() + { + _rloCreator = new Mock(); + _creator = new DynamicsCreator(_rloCreator.Object); + } + + [Fact] + public void should_return_nothing() + { + var fileConfig = new FileConfiguration(); + + this.Given(_ => GivenThe(fileConfig)) + .When(_ => WhenICreate()) + .Then(_ => ThenNothingIsReturned()) + .And(_ => ThenTheRloCreatorIsNotCalled()) + .BDDfy(); + } + + [Fact] + public void should_return_re_routes() + { + var fileConfig = new FileConfiguration + { + DynamicReRoutes = new List + { + new FileDynamicReRoute + { + ServiceName = "1", + RateLimitRule = new FileRateLimitRule + { + EnableRateLimiting = false + } + }, + new FileDynamicReRoute + { + ServiceName = "2", + RateLimitRule = new FileRateLimitRule + { + EnableRateLimiting = true + } + } + } + }; + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenTheRloCreatorReturns()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheReRoutesAreReturned()) + .And(_ => ThenTheRloCreatorIsCalledCorrectly()) + .BDDfy(); + } + + private void ThenTheRloCreatorIsCalledCorrectly() + { + _rloCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[0].RateLimitRule, + _fileConfig.GlobalConfiguration), Times.Once); + + _rloCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[1].RateLimitRule, + _fileConfig.GlobalConfiguration), Times.Once); + } + + private void ThenTheReRoutesAreReturned() + { + _result.Count.ShouldBe(2); + _result[0].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeFalse(); + _result[0].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo1); + _result[0].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[0].ServiceName); + + _result[1].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue(); + _result[1].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo2); + _result[1].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[1].ServiceName); + } + + private void GivenTheRloCreatorReturns() + { + _rlo1 = new RateLimitOptionsBuilder().Build(); + _rlo2 = new RateLimitOptionsBuilder().WithEnableRateLimiting(true).Build(); + + _rloCreator + .SetupSequence(x => x.Create(It.IsAny(), It.IsAny())) + .Returns(_rlo1) + .Returns(_rlo2); + } + + private void ThenTheRloCreatorIsNotCalled() + { + _rloCreator.Verify(x => x.Create(It.IsAny(), It.IsAny()), Times.Never); + } + + private void ThenNothingIsReturned() + { + _result.Count.ShouldBe(0); + } + + private void WhenICreate() + { + _result = _creator.Create(_fileConfig); + } + + private void GivenThe(FileConfiguration fileConfig) + { + _fileConfig = fileConfig; + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs index b9663a72..71d3fd9e 100644 --- a/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs @@ -2,1115 +2,126 @@ { using System.Collections.Generic; using Moq; - using Ocelot.Cache; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Creator; using Ocelot.Configuration.File; using Ocelot.Configuration.Validator; - using Ocelot.Logging; using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; using Xunit; using Ocelot.Errors; - using Ocelot.UnitTests.TestData; - using Ocelot.Values; - using System; + using System.Threading.Tasks; + using Ocelot.UnitTests.Responder; + using System.Linq; public class FileInternalConfigurationCreatorTests { private readonly Mock _validator; + private readonly Mock _reRoutesCreator; + private readonly Mock _aggregatesCreator; + private readonly Mock _dynamicsCreator; + private readonly Mock _configCreator; private Response _config; private FileConfiguration _fileConfiguration; - private readonly Mock _logger; - private readonly FileInternalConfigurationCreator _internalConfigurationCreator; - private readonly Mock _claimsToThingCreator; - private readonly Mock _authOptionsCreator; - private readonly Mock _upstreamTemplatePatternCreator; - private readonly Mock _requestIdKeyCreator; - private readonly Mock _serviceProviderConfigCreator; - private readonly Mock _qosOptionsCreator; - private readonly Mock _fileReRouteOptionsCreator; - private readonly Mock _rateLimitOptions; - private readonly Mock _regionCreator; - private readonly Mock _httpHandlerOptionsCreator; - private readonly Mock _serviceProvider; - private readonly Mock _headerFindAndReplaceCreator; - private readonly Mock _downstreamAddressesCreator; + private readonly FileInternalConfigurationCreator _creator; + private Response _result; + private List _reRoutes; + private List _aggregates; + private List _dynamics; + private InternalConfiguration _internalConfig; public FileInternalConfigurationCreatorTests() { - _logger = new Mock(); _validator = new Mock(); - _claimsToThingCreator = new Mock(); - _authOptionsCreator = new Mock(); - _upstreamTemplatePatternCreator = new Mock(); - _requestIdKeyCreator = new Mock(); - _serviceProviderConfigCreator = new Mock(); - _qosOptionsCreator = new Mock(); - _fileReRouteOptionsCreator = new Mock(); - _rateLimitOptions = new Mock(); - _regionCreator = new Mock(); - _httpHandlerOptionsCreator = new Mock(); - _serviceProvider = new Mock(); - _headerFindAndReplaceCreator = new Mock(); - _downstreamAddressesCreator = new Mock(); + _reRoutesCreator = new Mock(); + _aggregatesCreator = new Mock(); + _dynamicsCreator = new Mock(); + _configCreator = new Mock(); - _internalConfigurationCreator = new FileInternalConfigurationCreator( - _validator.Object, - _logger.Object, - _claimsToThingCreator.Object, - _authOptionsCreator.Object, - _upstreamTemplatePatternCreator.Object, - _requestIdKeyCreator.Object, - _serviceProviderConfigCreator.Object, - _qosOptionsCreator.Object, - _fileReRouteOptionsCreator.Object, - _rateLimitOptions.Object, - _regionCreator.Object, - _httpHandlerOptionsCreator.Object, - _serviceProvider.Object, - _headerFindAndReplaceCreator.Object, - _downstreamAddressesCreator.Object); + _creator = new FileInternalConfigurationCreator(_validator.Object, _reRoutesCreator.Object, _aggregatesCreator.Object, _dynamicsCreator.Object, _configCreator.Object); } [Fact] - public void should_set_up_sticky_sessions_config() + public void should_return_validation_error() { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); + var fileConfiguration = new FileConfiguration(); - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithDownstreamAddresses(new List() { new DownstreamHostAndPort("127.0.0.1", 80) }) - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithLoadBalancerKey("CookieStickySessions:sessionid") - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - } - }, - LoadBalancerOptions = new FileLoadBalancerOptions - { - Expiry = 10, - Key = "sessionid", - Type = "CookieStickySessions" - }, - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) + this.Given(_ => GivenThe(fileConfiguration)) + .And(_ => GivenTheValidationFails()) + .When(_ => WhenICreate()) + .Then(_ => ThenAnErrorIsReturned()) .BDDfy(); } [Fact] - public void should_set_up_aggregate_re_route() + public void should_return_internal_configuration() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura", - UpstreamHost = "localhost" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51880, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom", - UpstreamHost = "localhost", - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - }, - Aggregator = "asdf" - } - } - }; + var fileConfiguration = new FileConfiguration(); - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - var expected = new List(); - - var lauraDownstreamReRoute = new DownstreamReRouteBuilder() - .WithUpstreamHost("localhost") - .WithKey("Laura") - .WithDownstreamPathTemplate("/") - .WithDownstreamScheme("http") - .WithUpstreamHttpMethod(new List() {"Get"}) - .WithDownstreamAddresses(new List() {new DownstreamHostAndPort("localhost", 51878)}) - .WithLoadBalancerKey("/laura|Get|localhost:51878") - .Build(); - - var lauraReRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod(new List() { "Get" }) - .WithUpstreamHost("localhost") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/laura").Build()) - .WithDownstreamReRoute(lauraDownstreamReRoute) - .Build(); - - expected.Add(lauraReRoute); - - var tomDownstreamReRoute = new DownstreamReRouteBuilder() - .WithUpstreamHost("localhost") - .WithKey("Tom") - .WithDownstreamPathTemplate("/") - .WithDownstreamScheme("http") - .WithUpstreamHttpMethod(new List() { "Get" }) - .WithDownstreamAddresses(new List() { new DownstreamHostAndPort("localhost", 51878) }) - .WithLoadBalancerKey("/tom|Get|localhost:51880") - .Build(); - - var tomReRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod(new List() { "Get" }) - .WithUpstreamHost("localhost") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/tom").Build()) - .WithDownstreamReRoute(tomDownstreamReRoute) - .Build(); - - expected.Add(tomReRoute); - - var aggregateReReRoute = new ReRouteBuilder() - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/").Build()) - .WithUpstreamHost("localhost") - .WithDownstreamReRoute(lauraDownstreamReRoute) - .WithDownstreamReRoute(tomDownstreamReRoute) - .WithUpstreamHttpMethod(new List() { "Get" }) - .Build(); - - expected.Add(aggregateReReRoute); - - var tupleList = new List<(string, string)> - { - ("woop", "/laura"), - ("woop", "/laura"), - ("woop", "/tom"), - ("woop", "/tom"), - ("woop", "/"), - ("woop", "/") - }; - - this.Given(x => x.GivenTheConfigIs(configuration)) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns(tupleList.ToArray())) - .And(x => x.GivenTheFollowingOptionsAreReturned(new ReRouteOptionsBuilder().Build())) - .And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig)) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheConfigIsValid()) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheServiceProviderCreatorIsCalledCorrectly()) - .Then(x => x.ThenTheAggregateReRoutesAre(expected)) + this.Given(_ => GivenThe(fileConfiguration)) + .And(_ => GivenTheValidationSucceeds()) + .And(_ => GivenTheDependenciesAreSetUp()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDependenciesAreCalledCorrectly()) .BDDfy(); } - [Fact] - public void should_call_service_provider_config_creator() + private void ThenTheDependenciesAreCalledCorrectly() { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Host = "localhost", - Port = 8500, - } - } - })) - .And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig)) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheConfigIsValid()) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheServiceProviderCreatorIsCalledCorrectly()) - .BDDfy(); + _reRoutesCreator.Verify(x => x.Create(_fileConfiguration), Times.Once); + _aggregatesCreator.Verify(x => x.Create(_fileConfiguration, _reRoutes), Times.Once); + _dynamicsCreator.Verify(x => x.Create(_fileConfiguration), Times.Once); + + var mergedReRoutes = _reRoutes + .Union(_aggregates) + .Union(_dynamics) + .ToList(); + + _configCreator.Verify(x => x.Create(_fileConfiguration, It.Is>(y => y.Count == mergedReRoutes.Count)), Times.Once); } - [Fact] - public void should_call_region_creator() + private void GivenTheDependenciesAreSetUp() { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "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 => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingRegionIsReturned("region")) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheRegionCreatorIsCalledCorrectly()) - .And(x => x.ThenTheHeaderFindAndReplaceCreatorIsCalledCorrectly()) - .BDDfy(); + _reRoutes = new List { new ReRouteBuilder().Build() }; + _aggregates = new List { new ReRouteBuilder().Build() }; + _dynamics = new List { new ReRouteBuilder().Build() }; + _internalConfig = new InternalConfiguration(null, "", null, "", null, "", null, null); + + _reRoutesCreator.Setup(x => x.Create(It.IsAny())).Returns(_reRoutes); + _aggregatesCreator.Setup(x => x.Create(It.IsAny(), It.IsAny>())).Returns(_aggregates); + _dynamicsCreator.Setup(x => x.Create(It.IsAny())).Returns(_dynamics); + _configCreator.Setup(x => x.Create(It.IsAny(), It.IsAny>())).Returns(_internalConfig); } - [Fact] - public void should_call_rate_limit_options_creator() + private void GivenTheValidationSucceeds() { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - } - }, - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheRateLimitOptionsCreatorIsCalledCorrectly()) - .BDDfy(); + var ok = new ConfigurationValidationResult(false); + var response = new OkResponse(ok); + _validator.Setup(x => x.IsValid(It.IsAny())).ReturnsAsync(response); } - [Fact] - public void should_call_qos_options_creator() + private void ThenAnErrorIsReturned() { - var expected = new QoSOptionsBuilder() - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .WithTimeoutValue(1) - .Build(); - - var serviceOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - } - }, - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - DurationOfBreak = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions)) - .And(x => x.GivenTheQosOptionsCreatorReturns(expected)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheQosOptionsAre(expected)) - .BDDfy(); + _result.IsError.ShouldBeTrue(); } - [Fact] - public void should_use_downstream_host() + private async Task WhenICreate() { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithDownstreamAddresses(new List() {new DownstreamHostAndPort("127.0.0.1", 80)}) - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamHttpMethod(new List {"Get"}) - .WithLoadBalancerKey("/api/products/{productId}|Get|127.0.0.1:0") - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - } - }, - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) - .BDDfy(); + _result = await _creator.Create(_fileConfiguration); } - [Fact] - public void should_use_downstream_scheme() + private void GivenTheValidationFails() { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - var handlers = new List {"Polly", "Tracer"}; - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamScheme("https") - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List {"Get"}) - .WithDelegatingHandlers(handlers) - .WithLoadBalancerKey("/api/products/{productId}|Get|") - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamScheme = "https", - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - DelegatingHandlers = handlers - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) - .BDDfy(); + var error = new ConfigurationValidationResult(true, new List { new AnyError() }); + var response = new OkResponse(error); + _validator.Setup(x => x.IsValid(It.IsAny())).ReturnsAsync(response); } - [Fact] - public void should_use_service_discovery_for_downstream_service_host() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List {"Get"}) - .WithUseServiceDiscovery(true) - .WithServiceName("ProductService") - .WithLoadBalancerKey("/api/products/{productId}|Get|") - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = false, - ServiceName = "ProductService" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Host = "127.0.0.1" - } - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_not_use_service_discovery_for_downstream_host_url_when_no_service_name() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List {"Get"}) - .WithUseServiceDiscovery(false) - .WithLoadBalancerKey("/api/products/{productId}|Get|") - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = false, - } - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_call_template_pattern_creator_correctly() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List {"Get"}) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1, false, "/api/products/{productId}")) - .WithLoadBalancerKey("/api/products/{productId}|Get|") - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = false - } - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$", "/api/products/{productId}")) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1, false, "/api/products/{productId}")) - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_call_request_id_creator() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List {"Get"}) - .WithRequestIdKey("blahhhh") - .WithLoadBalancerKey("/api/products/{productId}|Get|") - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - RequestIdKey = "blahhhh" - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheRequestIdCreatorReturns("blahhhh")) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) - .And(x => x.ThenTheRequestIdKeyCreatorIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_call_httpHandler_creator() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - var httpHandlerOptions = new HttpHandlerOptions(true, true,false, true); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - } - }, - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" } - } - }, - })) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly()) - .BDDfy(); - } - - [Theory] - [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] - public void should_create_with_headers_to_extract(FileConfiguration fileConfig) - { - var reRouteOptions = new ReRouteOptionsBuilder() - .WithIsAuthenticated(true) - .Build(); - - var authenticationOptions = new AuthenticationOptionsBuilder() - .WithAllowedScopes(new List()) - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List {"Get"}) - .WithAuthenticationOptions(authenticationOptions) - .WithClaimsToHeaders(new List - { - new ClaimToThing("CustomerId", "CustomerId", "", 0), - }) - .WithLoadBalancerKey("/api/products/{productId}|Get|") - .Build(); - - var expected = new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - }; - - this.Given(x => x.GivenTheConfigIs(fileConfig)) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheClaimsToThingCreatorReturns(new List { new ClaimToThing("CustomerId", "CustomerId", "", 0) })) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(expected)) - .And(x => x.ThenTheAuthenticationOptionsAre(expected)) - .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) - .BDDfy(); - } - - [Theory] - [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] - public void should_create_with_authentication_properties(FileConfiguration fileConfig) - { - var reRouteOptions = new ReRouteOptionsBuilder() - .WithIsAuthenticated(true) - .Build(); - - var authenticationOptions = new AuthenticationOptionsBuilder() - .WithAllowedScopes(new List()) - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List {"Get"}) - .WithAuthenticationOptions(authenticationOptions) - .WithLoadBalancerKey("/api/products/{productId}|Get|") - .Build(); - - var expected = new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithTemplate("woop").WithOriginalValue("/api/products/{productId}").Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - }; - - this.Given(x => x.GivenTheConfigIs(fileConfig)) - .And(x => GivenTheUpstreamTemplatePatternCreatorReturns("woop", "/api/products/{productId}")) - .And(x => GivenTheDownstreamAddresses()) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(expected)) - .And(x => x.ThenTheAuthenticationOptionsAre(expected)) - .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_return_validation_errors() - { - var errors = new List {new FileValidationFailedError("some message")}; - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration())) - .And(x => GivenTheDownstreamAddresses()) - .And(x => x.GivenTheConfigIsInvalid(errors)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheErrorsAreReturned(errors)) - .BDDfy(); - } - - [Fact] - public void should_set_up_dynamic_re_routes() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - var rateLimitOptions = new RateLimitOptionsBuilder() - .WithRateLimitRule(new RateLimitRule("1s", 1, 1)) - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - DynamicReRoutes = new List - { - new FileDynamicReRoute - { - ServiceName = "test", - RateLimitRule = new FileRateLimitRule - { - Period = "1s", - PeriodTimespan = 1, - Limit = 1 - } - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => GivenTheRateLimitCreatorReturns(rateLimitOptions)) - .And(x => GivenTheDownstreamAddresses()) - .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheDynamicReRouteIsSetUp("test", rateLimitOptions.RateLimitRule)) - .BDDfy(); - } - - private void GivenTheRateLimitCreatorReturns(RateLimitOptions rateLimitOptions) - { - _rateLimitOptions - .Setup(x => x.Create(It.IsAny(), It.IsAny())) - .Returns(rateLimitOptions); - } - - private void GivenTheConfigIsInvalid(List errors) - { - _validator - .Setup(x => x.IsValid(It.IsAny())) - .ReturnsAsync(new OkResponse(new ConfigurationValidationResult(true, errors))); - } - - private void ThenTheErrorsAreReturned(List errors) - { - _config.IsError.ShouldBeTrue(); - _config.Errors[0].ShouldBe(errors[0]); - } - - private void GivenTheFollowingOptionsAreReturned(ReRouteOptions fileReRouteOptions) - { - _fileReRouteOptionsCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(fileReRouteOptions); - } - - private void ThenTheRateLimitOptionsCreatorIsCalledCorrectly() - { - _rateLimitOptions - .Verify(x => x.Create(It.IsAny(), It.IsAny()), Times.Once); - } - - private void GivenTheConfigIsValid() - { - _validator - .Setup(x => x.IsValid(It.IsAny())) - .ReturnsAsync(new OkResponse(new ConfigurationValidationResult(false))); - } - - private void GivenTheConfigIs(FileConfiguration fileConfiguration) + private void GivenThe(FileConfiguration fileConfiguration) { _fileConfiguration = fileConfiguration; } - - private void WhenICreateTheConfig() - { - _config = _internalConfigurationCreator.Create(_fileConfiguration).Result; - } - - private void ThenTheDynamicReRouteIsSetUp(string serviceName, RateLimitRule rateLimitOptions) - { - var dynamic = _config.Data.ReRoutes[0].DownstreamReRoute[0]; - dynamic.ServiceName.ShouldBe(serviceName); - dynamic.EnableEndpointEndpointRateLimiting.ShouldBeTrue(); - dynamic.RateLimitOptions.RateLimitRule.Period.ShouldBe(rateLimitOptions.Period); - dynamic.RateLimitOptions.RateLimitRule.Limit.ShouldBe(rateLimitOptions.Limit); - dynamic.RateLimitOptions.RateLimitRule.PeriodTimespan.ShouldBe(rateLimitOptions.PeriodTimespan); - } - - private void ThenTheAggregateReRoutesAre(List expectedReRoutes) - { - for (int i = 0; i < _config.Data.ReRoutes.Count; i++) - { - var result = _config.Data.ReRoutes[i]; - var expected = expectedReRoutes[i]; - - result.DownstreamReRoute.Count.ShouldBe(expected.DownstreamReRoute.Count); - - result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); - result.UpstreamTemplatePattern.OriginalValue.ShouldBe(expected.UpstreamTemplatePattern.OriginalValue); - result.UpstreamTemplatePattern.Template.ShouldBe(expected.UpstreamTemplatePattern.Template); - - result.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamReRoute[0].DownstreamPathTemplate.Value); - result.DownstreamReRoute[0].ClaimsToClaims.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToClaims.Count); - result.DownstreamReRoute[0].ClaimsToHeaders.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToHeaders.Count); - result.DownstreamReRoute[0].ClaimsToQueries.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToQueries.Count); - result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey); - result.DownstreamReRoute[0].LoadBalancerKey.ShouldBe(expected.DownstreamReRoute[0].LoadBalancerKey); - result.DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DownstreamReRoute[0].DelegatingHandlers); - result.DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToDownstream); - result.DownstreamReRoute[0].AddHeadersToUpstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToUpstream, "AddHeadersToUpstream should be set"); - } - } - - private void ThenTheReRoutesAre(List expectedReRoutes) - { - for (int i = 0; i < _config.Data.ReRoutes.Count; i++) - { - var result = _config.Data.ReRoutes[i]; - var expected = expectedReRoutes[i]; - - result.DownstreamReRoute.Count.ShouldBe(expected.DownstreamReRoute.Count); - - result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); - result.UpstreamTemplatePattern.OriginalValue.ShouldBe(expected.UpstreamTemplatePattern.OriginalValue); - result.UpstreamTemplatePattern.Template.ShouldBe(expected.UpstreamTemplatePattern.Template); - - result.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamReRoute[0].DownstreamPathTemplate.Value); - result.DownstreamReRoute[0].ClaimsToClaims.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToClaims.Count); - result.DownstreamReRoute[0].ClaimsToHeaders.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToHeaders.Count); - result.DownstreamReRoute[0].ClaimsToQueries.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToQueries.Count); - result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey); - result.DownstreamReRoute[0].LoadBalancerKey.ShouldBe(expected.DownstreamReRoute[0].LoadBalancerKey); - result.DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DownstreamReRoute[0].DelegatingHandlers); - result.DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToDownstream); - result.DownstreamReRoute[0].AddHeadersToUpstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToUpstream, "AddHeadersToUpstream should be set"); - } - } - - private void ThenTheAuthenticationOptionsAre(List expectedReRoutes) - { - for (int i = 0; i < _config.Data.ReRoutes.Count; i++) - { - var result = _config.Data.ReRoutes[i].DownstreamReRoute[0].AuthenticationOptions; - var expected = expectedReRoutes[i].DownstreamReRoute[0].AuthenticationOptions; - result.AllowedScopes.ShouldBe(expected.AllowedScopes); - } - } - - private void GivenTheClaimsToThingCreatorReturns(List claimsToThing) - { - _claimsToThingCreator - .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].AddHeadersToRequest)) - .Returns(claimsToThing); - } - - private void GivenTheAuthOptionsCreatorReturns(AuthenticationOptions authOptions) - { - _authOptionsCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(authOptions); - } - - private void ThenTheAuthOptionsCreatorIsCalledCorrectly() - { - _authOptionsCreator - .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); - } - - private void GivenTheUpstreamTemplatePatternCreatorReturns(string pattern, string original) - { - _upstreamTemplatePatternCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(new UpstreamPathTemplate(pattern, 1, false, original)); - } - - private void GivenTheUpstreamTemplatePatternCreatorReturns(params (string pattern, string original)[] list) - { - var builder = _upstreamTemplatePatternCreator - .SetupSequence(x => x.Create(It.IsAny())); - - foreach (var p in list) - { - builder.Returns(new UpstreamPathTemplate(p.pattern, 1, false, p.original)); - } - } - - private void ThenTheRequestIdKeyCreatorIsCalledCorrectly() - { - _requestIdKeyCreator - .Verify(x => x.Create(_fileConfiguration.ReRoutes[0], _fileConfiguration.GlobalConfiguration), Times.Once); - } - - private void GivenTheRequestIdCreatorReturns(string requestId) - { - _requestIdKeyCreator - .Setup(x => x.Create(It.IsAny(), It.IsAny())) - .Returns(requestId); - } - - private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions) - { - _qosOptionsCreator - .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].QoSOptions, It.IsAny(), It.IsAny())) - .Returns(qosOptions); - } - - private void ThenTheQosOptionsAre(QoSOptions qosOptions) - { - _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); - _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); - _config.Data.ReRoutes[0].DownstreamReRoute[0].QosOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); - } - - private void ThenTheServiceProviderCreatorIsCalledCorrectly() - { - _serviceProviderConfigCreator - .Verify(x => x.Create(_fileConfiguration.GlobalConfiguration), Times.Once); - } - - private void ThenTheHeaderFindAndReplaceCreatorIsCalledCorrectly() - { - _headerFindAndReplaceCreator - .Verify(x => x.Create(It.IsAny()), Times.Once); - } - - private void GivenTheHeaderFindAndReplaceCreatorReturns() - { - _headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny())).Returns(new HeaderTransformations(new List(), new List(), new List(), new List())); - } - - private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration) - { - _serviceProviderConfigCreator - .Setup(x => x.Create(It.IsAny())).Returns(serviceProviderConfiguration); - } - - private void GivenTheFollowingRegionIsReturned(string region) - { - _regionCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(region); - } - - private void ThenTheRegionCreatorIsCalledCorrectly() - { - _regionCreator - .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); - } - - private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions) - { - _httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny())) - .Returns(httpHandlerOptions); - } - - private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly() - { - _httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0].HttpHandlerOptions), Times.Once()); - } - - private void GivenTheDownstreamAddresses() - { - _downstreamAddressesCreator.Setup(x => x.Create(It.IsAny())).Returns(new List()); - } } } diff --git a/test/Ocelot.UnitTests/Configuration/LoadBalancerOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/LoadBalancerOptionsCreatorTests.cs new file mode 100644 index 00000000..8429bde4 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/LoadBalancerOptionsCreatorTests.cs @@ -0,0 +1,54 @@ +namespace Ocelot.UnitTests.Configuration +{ + using Ocelot.Configuration; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class LoadBalancerOptionsCreatorTests + { + private readonly ILoadBalancerOptionsCreator _creator; + private FileLoadBalancerOptions _fileLoadBalancerOptions; + private LoadBalancerOptions _result; + + public LoadBalancerOptionsCreatorTests() + { + _creator = new LoadBalancerOptionsCreator(); + } + + [Fact] + public void should_create() + { + var fileLoadBalancerOptions = new FileLoadBalancerOptions + { + Type = "test", + Key = "west", + Expiry = 1 + }; + + this.Given(_ => GivenThe(fileLoadBalancerOptions)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheOptionsAreCreated(fileLoadBalancerOptions)) + .BDDfy(); + } + + private void ThenTheOptionsAreCreated(FileLoadBalancerOptions expected) + { + _result.Type.ShouldBe(expected.Type); + _result.Key.ShouldBe(expected.Key); + _result.ExpiryInMs.ShouldBe(expected.Expiry); + } + + private void WhenICreate() + { + _result = _creator.Create(_fileLoadBalancerOptions); + } + + private void GivenThe(FileLoadBalancerOptions fileLoadBalancerOptions) + { + _fileLoadBalancerOptions = fileLoadBalancerOptions; + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ReRouteKeyCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ReRouteKeyCreatorTests.cs new file mode 100644 index 00000000..f1bb26d9 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/ReRouteKeyCreatorTests.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Ocelot.LoadBalancer.LoadBalancers; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + public class ReRouteKeyCreatorTests + { + private ReRouteKeyCreator _creator; + private FileReRoute _reRoute; + private string _result; + + public ReRouteKeyCreatorTests() + { + _creator = new ReRouteKeyCreator(); + } + + [Fact] + public void should_return_sticky_session_key() + { + var reRoute = new FileReRoute + { + LoadBalancerOptions = new FileLoadBalancerOptions + { + Key = "testy", + Type = nameof(CookieStickySessions) + } + }; + + this.Given(_ => GivenThe(reRoute)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheResultIs($"{nameof(CookieStickySessions)}:{reRoute.LoadBalancerOptions.Key}")) + .BDDfy(); + } + + [Fact] + public void should_return_re_route_key() + { + var reRoute = new FileReRoute + { + UpstreamPathTemplate = "/api/product", + UpstreamHttpMethod = new List {"GET", "POST", "PUT"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 123 + }, + new FileHostAndPort + { + Host = "localhost", + Port = 123 + } + } + }; + + this.Given(_ => GivenThe(reRoute)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheResultIs($"{reRoute.UpstreamPathTemplate}|{string.Join(",", reRoute.UpstreamHttpMethod)}|{string.Join(",", reRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}")) + .BDDfy(); + } + + private void GivenThe(FileReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenICreate() + { + _result = _creator.Create(_reRoute); + } + + private void ThenTheResultIs(string expected) + { + _result.ShouldBe(expected); + } + + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs index d23aabc0..8230d6f1 100644 --- a/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs @@ -1,77 +1,80 @@ -using System.Collections.Generic; -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - public class ReRouteOptionsCreatorTests - { - private ReRouteOptionsCreator _creator; - private FileReRoute _reRoute; - private ReRouteOptions _result; - - public ReRouteOptionsCreatorTests() - { - _creator = new ReRouteOptionsCreator(); - } - - [Fact] - public void should_create_re_route_options() - { - var reRoute = new FileReRoute - { - RateLimitOptions = new FileRateLimitRule - { - EnableRateLimiting = true - }, - AuthenticationOptions = new FileAuthenticationOptions() - { - AuthenticationProviderKey = "Test" - }, - RouteClaimsRequirement = new Dictionary() - { - {"",""} - }, - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 1 - } - }; - - var expected = new ReRouteOptionsBuilder() - .WithIsAuthenticated(true) - .WithIsAuthorised(true) - .WithIsCached(true) - .WithRateLimiting(true) - .Build(); - - this.Given(x => x.GivenTheFollowing(reRoute)) - .When(x => x.WhenICreate()) - .Then(x => x.ThenTheFollowingIsReturned(expected)) - .BDDfy(); - } - - private void GivenTheFollowing(FileReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenICreate() - { - _result = _creator.Create(_reRoute); - } - - private void ThenTheFollowingIsReturned(ReRouteOptions expected) - { - _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated); - _result.IsAuthorised.ShouldBe(expected.IsAuthorised); - _result.IsCached.ShouldBe(expected.IsCached); - _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); - } - } +namespace Ocelot.UnitTests.Configuration +{ + using System.Collections.Generic; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class ReRouteOptionsCreatorTests + { + private readonly ReRouteOptionsCreator _creator; + private FileReRoute _reRoute; + private ReRouteOptions _result; + + public ReRouteOptionsCreatorTests() + { + _creator = new ReRouteOptionsCreator(); + } + + [Fact] + public void should_create_re_route_options() + { + var reRoute = new FileReRoute + { + RateLimitOptions = new FileRateLimitRule + { + EnableRateLimiting = true + }, + AuthenticationOptions = new FileAuthenticationOptions() + { + AuthenticationProviderKey = "Test" + }, + RouteClaimsRequirement = new Dictionary() + { + {"",""} + }, + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 1 + }, + ServiceName = "west" + }; + + var expected = new ReRouteOptionsBuilder() + .WithIsAuthenticated(true) + .WithIsAuthorised(true) + .WithIsCached(true) + .WithRateLimiting(true) + .WithUseServiceDiscovery(true) + .Build(); + + this.Given(x => x.GivenTheFollowing(reRoute)) + .When(x => x.WhenICreate()) + .Then(x => x.ThenTheFollowingIsReturned(expected)) + .BDDfy(); + } + + private void GivenTheFollowing(FileReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenICreate() + { + _result = _creator.Create(_reRoute); + } + + private void ThenTheFollowingIsReturned(ReRouteOptions expected) + { + _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated); + _result.IsAuthorised.ShouldBe(expected.IsAuthorised); + _result.IsCached.ShouldBe(expected.IsCached); + _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); + _result.UseServiceDiscovery.ShouldBe(expected.UseServiceDiscovery); + } + } } diff --git a/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs new file mode 100644 index 00000000..7d27f376 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs @@ -0,0 +1,271 @@ +namespace Ocelot.UnitTests.Configuration +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Moq; + using Ocelot.Cache; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Ocelot.Values; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class ReRoutesCreatorTests + { + private ReRoutesCreator _creator; + private Mock _cthCreator; + private Mock _aoCreator; + private Mock _utpCreator; + private Mock _ridkCreator; + private Mock _qosoCreator; + private Mock _rroCreator; + private Mock _rloCreator; + private Mock _rCreator; + private Mock _hhoCreator; + private Mock _hfarCreator; + private Mock _daCreator; + private Mock _lboCreator; + private Mock _rrkCreator; + private FileConfiguration _fileConfig; + private ReRouteOptions _rro; + private string _requestId; + private string _rrk; + private UpstreamPathTemplate _upt; + private AuthenticationOptions _ao; + private List _ctt; + private QoSOptions _qoso; + private RateLimitOptions _rlo; + private string _region; + private HttpHandlerOptions _hho; + private HeaderTransformations _ht; + private List _dhp; + private LoadBalancerOptions _lbo; + private List _result; + + public ReRoutesCreatorTests() + { + _cthCreator = new Mock(); + _aoCreator = new Mock(); + _utpCreator = new Mock(); + _ridkCreator = new Mock(); + _qosoCreator = new Mock(); + _rroCreator = new Mock(); + _rloCreator = new Mock(); + _rCreator = new Mock(); + _hhoCreator = new Mock(); + _hfarCreator = new Mock(); + _daCreator = new Mock(); + _lboCreator = new Mock(); + _rrkCreator = new Mock(); + + _creator = new ReRoutesCreator( + _cthCreator.Object, + _aoCreator.Object, + _utpCreator.Object, + _ridkCreator.Object, + _qosoCreator.Object, + _rroCreator.Object, + _rloCreator.Object, + _rCreator.Object, + _hhoCreator.Object, + _hfarCreator.Object, + _daCreator.Object, + _lboCreator.Object, + _rrkCreator.Object + ); + } + + [Fact] + public void should_return_nothing() + { + var fileConfig = new FileConfiguration(); + + this.Given(_ => GivenThe(fileConfig)) + .When(_ => WhenICreate()) + .Then(_ => ThenNoReRoutesAreReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_re_routes() + { + var fileConfig = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + ServiceName = "dave", + DangerousAcceptAnyServerCertificateValidator = true, + AddClaimsToRequest = new Dictionary + { + { "a","b" } + }, + AddHeadersToRequest = new Dictionary + { + { "c","d" } + }, + AddQueriesToRequest = new Dictionary + { + { "e","f" } + }, + UpstreamHttpMethod = new List { "GET", "POST" } + }, + new FileReRoute + { + ServiceName = "wave", + DangerousAcceptAnyServerCertificateValidator = false, + AddClaimsToRequest = new Dictionary + { + { "g","h" } + }, + AddHeadersToRequest = new Dictionary + { + { "i","j" } + }, + AddQueriesToRequest = new Dictionary + { + { "k","l" } + }, + UpstreamHttpMethod = new List { "PUT", "DELETE" } + } + } + }; + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenTheDependenciesAreSetUpCorrectly()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDependenciesAreCalledCorrectly()) + .And(_ => ThenTheReRoutesAreCreated()) + .BDDfy(); + } + + private void ThenTheDependenciesAreCalledCorrectly() + { + ThenTheDepsAreCalledFor(_fileConfig.ReRoutes[0], _fileConfig.GlobalConfiguration); + ThenTheDepsAreCalledFor(_fileConfig.ReRoutes[1], _fileConfig.GlobalConfiguration); + } + + private void GivenTheDependenciesAreSetUpCorrectly() + { + _rro = new ReRouteOptions(false, false, false, false, false); + _requestId = "testy"; + _rrk = "besty"; + _upt = new UpstreamPathTemplateBuilder().Build(); + _ao = new AuthenticationOptionsBuilder().Build(); + _ctt = new List(); + _qoso = new QoSOptionsBuilder().Build(); + _rlo = new RateLimitOptionsBuilder().Build(); + _region = "vesty"; + _hho = new HttpHandlerOptionsBuilder().Build(); + _ht = new HeaderTransformations(new List(), new List(), new List(), new List()); + _dhp = new List(); + _lbo = new LoadBalancerOptionsBuilder().Build(); + + _rroCreator.Setup(x => x.Create(It.IsAny())).Returns(_rro); + _ridkCreator.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(_requestId); + _rrkCreator.Setup(x => x.Create(It.IsAny())).Returns(_rrk); + _utpCreator.Setup(x => x.Create(It.IsAny())).Returns(_upt); + _aoCreator.Setup(x => x.Create(It.IsAny())).Returns(_ao); + _cthCreator.Setup(x => x.Create(It.IsAny>())).Returns(_ctt); + _qosoCreator.Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())).Returns(_qoso); + _rloCreator.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(_rlo); + _rCreator.Setup(x => x.Create(It.IsAny())).Returns(_region); + _hhoCreator.Setup(x => x.Create(It.IsAny())).Returns(_hho); + _hfarCreator.Setup(x => x.Create(It.IsAny())).Returns(_ht); + _daCreator.Setup(x => x.Create(It.IsAny())).Returns(_dhp); + _lboCreator.Setup(x => x.Create(It.IsAny())).Returns(_lbo); + } + + private void ThenTheReRoutesAreCreated() + { + _result.Count.ShouldBe(2); + + ThenTheReRouteIsSet(_fileConfig.ReRoutes[0], 0); + ThenTheReRouteIsSet(_fileConfig.ReRoutes[1], 1); + } + + private void ThenNoReRoutesAreReturned() + { + _result.ShouldBeEmpty(); + } + + private void GivenThe(FileConfiguration fileConfig) + { + _fileConfig = fileConfig; + } + + private void WhenICreate() + { + _result = _creator.Create(_fileConfig); + } + + private void ThenTheReRouteIsSet(FileReRoute expected, int reRouteIndex) + { + _result[reRouteIndex].DownstreamReRoute[0].IsAuthenticated.ShouldBe(_rro.IsAuthenticated); + _result[reRouteIndex].DownstreamReRoute[0].IsAuthorised.ShouldBe(_rro.IsAuthorised); + _result[reRouteIndex].DownstreamReRoute[0].IsCached.ShouldBe(_rro.IsCached); + _result[reRouteIndex].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBe(_rro.EnableRateLimiting); + _result[reRouteIndex].DownstreamReRoute[0].RequestIdKey.ShouldBe(_requestId); + _result[reRouteIndex].DownstreamReRoute[0].LoadBalancerKey.ShouldBe(_rrk); + _result[reRouteIndex].DownstreamReRoute[0].UpstreamPathTemplate.ShouldBe(_upt); + _result[reRouteIndex].DownstreamReRoute[0].AuthenticationOptions.ShouldBe(_ao); + _result[reRouteIndex].DownstreamReRoute[0].ClaimsToHeaders.ShouldBe(_ctt); + _result[reRouteIndex].DownstreamReRoute[0].ClaimsToQueries.ShouldBe(_ctt); + _result[reRouteIndex].DownstreamReRoute[0].ClaimsToClaims.ShouldBe(_ctt); + _result[reRouteIndex].DownstreamReRoute[0].QosOptions.ShouldBe(_qoso); + _result[reRouteIndex].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo); + _result[reRouteIndex].DownstreamReRoute[0].CacheOptions.Region.ShouldBe(_region); + _result[reRouteIndex].DownstreamReRoute[0].CacheOptions.TtlSeconds.ShouldBe(expected.FileCacheOptions.TtlSeconds); + _result[reRouteIndex].DownstreamReRoute[0].HttpHandlerOptions.ShouldBe(_hho); + _result[reRouteIndex].DownstreamReRoute[0].UpstreamHeadersFindAndReplace.ShouldBe(_ht.Upstream); + _result[reRouteIndex].DownstreamReRoute[0].DownstreamHeadersFindAndReplace.ShouldBe(_ht.Downstream); + _result[reRouteIndex].DownstreamReRoute[0].AddHeadersToUpstream.ShouldBe(_ht.AddHeadersToUpstream); + _result[reRouteIndex].DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(_ht.AddHeadersToDownstream); + _result[reRouteIndex].DownstreamReRoute[0].DownstreamAddresses.ShouldBe(_dhp); + _result[reRouteIndex].DownstreamReRoute[0].LoadBalancerOptions.ShouldBe(_lbo); + _result[reRouteIndex].DownstreamReRoute[0].UseServiceDiscovery.ShouldBe(_rro.UseServiceDiscovery); + _result[reRouteIndex].DownstreamReRoute[0].DangerousAcceptAnyServerCertificateValidator.ShouldBe(expected.DangerousAcceptAnyServerCertificateValidator); + _result[reRouteIndex].DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DelegatingHandlers); + _result[reRouteIndex].DownstreamReRoute[0].ServiceName.ShouldBe(expected.ServiceName); + _result[reRouteIndex].DownstreamReRoute[0].DownstreamScheme.ShouldBe(expected.DownstreamScheme); + _result[reRouteIndex].DownstreamReRoute[0].RouteClaimsRequirement.ShouldBe(expected.RouteClaimsRequirement); + _result[reRouteIndex].DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate); + _result[reRouteIndex].DownstreamReRoute[0].Key.ShouldBe(expected.Key); + _result[reRouteIndex].UpstreamHttpMethod + .Select(x => x.Method) + .ToList() + .ShouldContain(x => x == expected.UpstreamHttpMethod[0]); + _result[reRouteIndex].UpstreamHttpMethod + .Select(x => x.Method) + .ToList() + .ShouldContain(x => x == expected.UpstreamHttpMethod[1]); + _result[reRouteIndex].UpstreamHost.ShouldBe(expected.UpstreamHost); + _result[reRouteIndex].DownstreamReRoute.Count.ShouldBe(1); + _result[reRouteIndex].UpstreamTemplatePattern.ShouldBe(_upt); + } + + private void ThenTheDepsAreCalledFor(FileReRoute fileReRoute, FileGlobalConfiguration globalConfig) + { + _rroCreator.Verify(x => x.Create(fileReRoute), Times.Once); + _ridkCreator.Verify(x => x.Create(fileReRoute, globalConfig), Times.Once); + _rrkCreator.Verify(x => x.Create(fileReRoute), Times.Once); + _utpCreator.Verify(x => x.Create(fileReRoute), Times.Exactly(2)); + _aoCreator.Verify(x => x.Create(fileReRoute), Times.Once); + _cthCreator.Verify(x => x.Create(fileReRoute.AddHeadersToRequest), Times.Once); + _cthCreator.Verify(x => x.Create(fileReRoute.AddClaimsToRequest), Times.Once); + _cthCreator.Verify(x => x.Create(fileReRoute.AddQueriesToRequest), Times.Once); + _qosoCreator.Verify(x => x.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod)); + _rloCreator.Verify(x => x.Create(fileReRoute.RateLimitOptions, globalConfig), Times.Once); + _rCreator.Verify(x => x.Create(fileReRoute), Times.Once); + _hhoCreator.Verify(x => x.Create(fileReRoute.HttpHandlerOptions), Times.Once); + _hfarCreator.Verify(x => x.Create(fileReRoute), Times.Once); + _daCreator.Verify(x => x.Create(fileReRoute), Times.Once); + _lboCreator.Verify(x => x.Create(fileReRoute.LoadBalancerOptions), Times.Once); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs index fe7ac7dd..25667638 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs @@ -37,7 +37,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder _handlerOptions = new HttpHandlerOptionsBuilder().Build(); _loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build(); _qosOptionsCreator - .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(_qoSOptions); _creator = new DownstreamRouteCreator(_qosOptionsCreator.Object); } @@ -198,7 +198,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder private void GivenTheQosCreatorReturns(QoSOptions options) { _qosOptionsCreator - .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(options); } @@ -260,7 +260,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldBe(expected); _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.UseQos.ShouldBeTrue(); _qosOptionsCreator - .Verify(x => x.Create(expected, _upstreamUrlPath, It.IsAny()), Times.Once); + .Verify(x => x.Create(expected, _upstreamUrlPath, It.IsAny>()), Times.Once); } private void GivenTheConfiguration(IInternalConfiguration config) diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 92555690..97690a29 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -459,7 +459,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) @@ -545,7 +544,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) @@ -556,7 +554,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { }) // empty list of methods .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") .Build()) .WithUpstreamHttpMethod(new List { }) // empty list of methods .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) @@ -587,7 +584,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List()) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") .Build()) .WithUpstreamHttpMethod(new List()) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) @@ -618,7 +614,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List()) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") .Build()) .WithUpstreamHttpMethod(new List()) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) @@ -659,7 +654,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") .Build()) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) diff --git a/test/Ocelot.UnitTests/TestData/AuthenticationConfigTestData.cs b/test/Ocelot.UnitTests/TestData/AuthenticationConfigTestData.cs deleted file mode 100644 index 0ae4096a..00000000 --- a/test/Ocelot.UnitTests/TestData/AuthenticationConfigTestData.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace Ocelot.UnitTests.TestData -{ - using System.Collections.Generic; - using Ocelot.Configuration.File; - - public class AuthenticationConfigTestData - { - public static IEnumerable GetAuthenticationData() - { - yield return new object[] - { - new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List(), - }, - AddHeadersToRequest = - { - { "CustomerId", "Claims[CustomerId] > value" }, - } - } - } - } - }; - - yield return new object[] - { - new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List(), - }, - AddHeadersToRequest = - { - { "CustomerId", "Claims[CustomerId] > value" }, - } - } - } - } - }; - } - } -}