From c8b72f31b5d06c30a1927ac4d9c1b055e38c156a Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Mon, 9 Jul 2018 08:08:39 +0100 Subject: [PATCH] #438 removed singleton delegating handlers as you cannot have these (#456) --- docs/features/delegatinghandlers.rst | 145 ++- global.json | 12 +- .../DependencyInjection/IOcelotBuilder.cs | 5 +- .../DependencyInjection/OcelotBuilder.cs | 21 +- .../HttpDelegatingHandlersTests.cs | 62 ++ test/Ocelot.AcceptanceTests/Steps.cs | 41 +- test/Ocelot.ManualTest/Program.cs | 23 +- .../DependencyInjection/OcelotBuilderTests.cs | 935 +++++++++--------- 8 files changed, 643 insertions(+), 601 deletions(-) diff --git a/docs/features/delegatinghandlers.rst b/docs/features/delegatinghandlers.rst index 445a46fb..7c354760 100644 --- a/docs/features/delegatinghandlers.rst +++ b/docs/features/delegatinghandlers.rst @@ -1,79 +1,66 @@ -Delegating Handers -================== - -Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 `_ -and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #264 `_. - -Usage -^^^^^ - -In order to add delegating handlers to the HttpClient transport you need to do two main things. - -First in order to create a class that can be used a delegating handler it must look as follows. We are going to register these handlers in the -asp.net core container so you can inject any other services you have registered into the constructor of your handler. - -.. code-block:: csharp - - public class FakeHandler : DelegatingHandler - { - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - //do stuff and optionally call the base handler.. - return await base.SendAsync(request, cancellationToken); - } - } - -Next you must add the handlers to Ocelot's container either as singleton like follows.. - -.. code-block:: csharp - - services.AddOcelot() - .AddSingletonDelegatingHandler() - .AddSingletonDelegatingHandler() - -Or transient as below... - -.. code-block:: csharp - - services.AddOcelot() - .AddTransientDelegatingHandler() - .AddTransientDelegatingHandler() - -Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of -the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true -then it becomes a global handler and will be applied to all ReRoutes. - -e.g. - -.. code-block:: csharp - - services.AddOcelot() - .AddSingletonDelegatingHandler(true) - -Or transient as below... - -.. code-block:: csharp - - services.AddOcelot() - .AddTransientDelegatingHandler(true) - -Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers -then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your -DelegatingHandlers for Ocelot to match them together. - -.. code-block:: json - - "DelegatingHandlers": [ - "FakeHandlerTwo", - "FakeHandler" - ] - -You can have as many DelegatingHandlers as you want and they are run in the following order: - -1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from ocelot.json. -2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from ocelot.json ordered as they are in the DelegatingHandlers array. -3. Tracing DelegatingHandler if enabled (see tracing docs). -4. QoS DelegatingHandler if enabled (see QoS docs). -5. The HttpClient sends the HttpRequestMessage. - -Hopefully other people will find this feature useful! +Delegating Handlers +=================== + +Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 `_ +and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #264 `_. + +Usage +^^^^^ + +In order to add delegating handlers to the HttpClient transport you need to do two main things. + +First in order to create a class that can be used a delegating handler it must look as follows. We are going to register these handlers in the +asp.net core container so you can inject any other services you have registered into the constructor of your handler. + +.. code-block:: csharp + + public class FakeHandler : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + //do stuff and optionally call the base handler.. + return await base.SendAsync(request, cancellationToken); + } + } + +Next you must add the handlers to Ocelot's container like below... + +.. code-block:: csharp + + services.AddOcelot() + .AddDelegatingHandler() + .AddDelegatingHandler() + +Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of +the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true +then it becomes a global handler and will be applied to all ReRoutes. + +e.g. + +As below... + +.. code-block:: csharp + + services.AddOcelot() + .AddDelegatingHandler(true) + +Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers +then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your +DelegatingHandlers for Ocelot to match them together. + +.. code-block:: json + + "DelegatingHandlers": [ + "FakeHandlerTwo", + "FakeHandler" + ] + +You can have as many DelegatingHandlers as you want and they are run in the following order: + +1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from ocelot.json. +2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from ocelot.json ordered as they are in the DelegatingHandlers array. +3. Tracing DelegatingHandler if enabled (see tracing docs). +4. QoS DelegatingHandler if enabled (see QoS docs). +5. The HttpClient sends the HttpRequestMessage. + +Hopefully other people will find this feature useful! diff --git a/global.json b/global.json index ceb6f979..1494d180 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ -{ - "projects": [ "src", "test" ], - "sdk": { - "version": "2.1.300" - } -} \ No newline at end of file +{ + "projects": [ "src", "test" ], + "sdk": { + "version": "2.1.301" + } +} diff --git a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs index 7cf514cb..20eafd36 100644 --- a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs @@ -19,10 +19,7 @@ namespace Ocelot.DependencyInjection IOcelotAdministrationBuilder AddAdministration(string path, Action configOptions); - IOcelotBuilder AddSingletonDelegatingHandler(bool global = false) - where T : DelegatingHandler; - - IOcelotBuilder AddTransientDelegatingHandler(bool global = false) + IOcelotBuilder AddDelegatingHandler(bool global = false) where T : DelegatingHandler; IOcelotBuilder AddSingletonDefinedAggregator() diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 7132c0b2..1b93ddbb 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -212,26 +212,7 @@ namespace Ocelot.DependencyInjection return this; } - public IOcelotBuilder AddSingletonDelegatingHandler(bool global = false) - where THandler : DelegatingHandler - { - if(global) - { - _services.AddSingleton(); - _services.AddSingleton(s => { - var service = s.GetService(); - return new GlobalDelegatingHandler(service); - }); - } - else - { - _services.AddSingleton(); - } - - return this; - } - - public IOcelotBuilder AddTransientDelegatingHandler(bool global = false) + public IOcelotBuilder AddDelegatingHandler(bool global = false) where THandler : DelegatingHandler { if(global) diff --git a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs index 740ee6e7..a6a9a9ce 100644 --- a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs @@ -101,6 +101,57 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_call_global_di_handlers_multiple_times() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 9187, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:9187", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlerRegisteredInDi()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + [Fact] public void should_call_global_di_handlers_with_dependency() { @@ -198,6 +249,17 @@ namespace Ocelot.AcceptanceTests return base.SendAsync(request, cancellationToken); } } + // ReSharper disable once ClassNeverInstantiated.Local + private class FakeHandlerAgain : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + Console.WriteLine(request.RequestUri); + + //do stuff and optionally call the base handler.. + return await base.SendAsync(request, cancellationToken); + } + } private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) { diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 5dd95831..62d1c816 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -237,8 +237,8 @@ namespace Ocelot.AcceptanceTests { s.AddSingleton(_webHostBuilder); s.AddOcelot() - .AddSingletonDelegatingHandler() - .AddSingletonDelegatingHandler(); + .AddDelegatingHandler() + .AddDelegatingHandler(); }) .Configure(a => { @@ -303,8 +303,39 @@ namespace Ocelot.AcceptanceTests { s.AddSingleton(_webHostBuilder); s.AddOcelot() - .AddSingletonDelegatingHandler(true) - .AddSingletonDelegatingHandler(true); + .AddDelegatingHandler(true) + .AddDelegatingHandler(true); + }) + .Configure(a => + { + a.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void GivenOcelotIsRunningWithGlobalHandlerRegisteredInDi() + where TOne : DelegatingHandler + { + _webHostBuilder = new WebHostBuilder(); + + _webHostBuilder + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); + config.AddJsonFile("ocelot.json"); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(s => + { + s.AddSingleton(_webHostBuilder); + s.AddOcelot() + .AddDelegatingHandler(true); }) .Configure(a => { @@ -336,7 +367,7 @@ namespace Ocelot.AcceptanceTests s.AddSingleton(_webHostBuilder); s.AddSingleton(dependency); s.AddOcelot() - .AddSingletonDelegatingHandler(true); + .AddDelegatingHandler(true); }) .Configure(a => { diff --git a/test/Ocelot.ManualTest/Program.cs b/test/Ocelot.ManualTest/Program.cs index a33395c1..fede584f 100644 --- a/test/Ocelot.ManualTest/Program.cs +++ b/test/Ocelot.ManualTest/Program.cs @@ -9,6 +9,9 @@ using Ocelot.Middleware; using System; using IdentityServer4.AccessTokenValidation; + using System.Net.Http; + using System.Threading.Tasks; + using System.Threading; public class Program { @@ -35,10 +38,11 @@ }); s.AddOcelot() - .AddCacheManager(x => - { - x.WithDictionaryHandle(); - }) + .AddDelegatingHandler(true) + // .AddCacheManager(x => + // { + // x.WithDictionaryHandle(); + // }) /*.AddOpenTracing(option => { option.CollectorUrl = "http://localhost:9618"; @@ -60,4 +64,15 @@ .Run(); } } + + public class FakeHandler : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + Console.WriteLine(request.RequestUri); + + //do stuff and optionally call the base handler.. + return await base.SendAsync(request, cancellationToken); + } + } } diff --git a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs index b295a564..358dab18 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs @@ -1,483 +1,452 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using CacheManager.Core; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Internal; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Ocelot.Cache; -using Ocelot.Configuration; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Setter; -using Ocelot.DependencyInjection; -using Ocelot.Requester; -using Ocelot.UnitTests.Requester; -using Shouldly; -using IdentityServer4.AccessTokenValidation; -using TestStack.BDDfy; -using Xunit; -using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests; -using Ocelot.Middleware.Multiplexer; - -namespace Ocelot.UnitTests.DependencyInjection -{ - public class OcelotBuilderTests - { - private readonly IServiceCollection _services; - private IServiceProvider _serviceProvider; - private readonly IConfiguration _configRoot; - private IOcelotBuilder _ocelotBuilder; - private readonly int _maxRetries; - private Exception _ex; - - public OcelotBuilderTests() - { - _configRoot = new ConfigurationRoot(new List()); - _services = new ServiceCollection(); - _services.AddSingleton(); - _services.AddSingleton(_configRoot); - _maxRetries = 100; - } - - [Fact] - public void should_add_specific_delegating_handlers_transient() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddSpecificTransientDelegatingHandler()) - .And(x => AddSpecificTransientDelegatingHandler()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) - .And(x => ThenTheSpecificHandlersAreTransient()) - .BDDfy(); - } - - [Fact] - public void should_add_specific_delegating_handler_singleton() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddSpecificDelegatingHandler()) - .And(x => AddSpecificDelegatingHandler()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) - .And(x => ThenTheSpecificHandlersAreSingleton()) - .BDDfy(); - } - - [Fact] - public void should_add_global_delegating_handlers_transient() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddTransientGlobalDelegatingHandler()) - .And(x => AddTransientGlobalDelegatingHandler()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) - .And(x => ThenTheGlobalHandlersAreTransient()) - .BDDfy(); - } - - [Fact] - public void should_add_global_delegating_handlers_singleton() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddGlobalDelegatingHandler()) - .And(x => AddGlobalDelegatingHandler()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) - .And(x => ThenTheGlobalHandlersAreSingleton()) - .BDDfy(); - } - - [Fact] - public void should_set_up_services() - { - this.When(x => WhenISetUpOcelotServices()) - .Then(x => ThenAnExceptionIsntThrown()) - .BDDfy(); - } - - [Fact] - public void should_return_ocelot_builder() - { - this.When(x => WhenISetUpOcelotServices()) - .Then(x => ThenAnOcelotBuilderIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_set_up_cache_manager() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpCacheManager()) - .Then(x => ThenAnExceptionIsntThrown()) - .And(x => OnlyOneVersionOfEachCacheIsRegistered()) - .BDDfy(); - } - - [Fact] - public void should_set_up_consul() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpConsul()) - .Then(x => ThenAnExceptionIsntThrown()) - .BDDfy(); - } - - [Fact] - public void should_set_up_rafty() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpRafty()) - .Then(x => ThenAnExceptionIsntThrown()) - .Then(x => ThenTheCorrectAdminPathIsRegitered()) - .BDDfy(); - } - - [Fact] - public void should_set_up_administration_with_identity_server_options() - { - Action options = o => {}; - - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpAdministration(options)) - .Then(x => ThenAnExceptionIsntThrown()) - .Then(x => ThenTheCorrectAdminPathIsRegitered()) - .BDDfy(); - } - - [Fact] - public void should_set_up_administration() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpAdministration()) - .Then(x => ThenAnExceptionIsntThrown()) - .Then(x => ThenTheCorrectAdminPathIsRegitered()) - .BDDfy(); - } - - [Fact] - public void should_use_logger_factory() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenIValidateScopes()) - .When(x => WhenIAccessLoggerFactory()) - .Then(x => ThenAnExceptionIsntThrown()) - .BDDfy(); - } - - [Fact] - public void should_set_up_tracing() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpOpentracing()) - .When(x => WhenIAccessOcelotHttpTracingHandler()) - .BDDfy(); - } - - [Fact] - public void should_set_up_without_passing_in_config() - { - this.When(x => WhenISetUpOcelotServicesWithoutConfig()) - .Then(x => ThenAnExceptionIsntThrown()) - .BDDfy(); - } - - [Fact] - public void should_add_singleton_defined_aggregators() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddSingletonDefinedAggregator()) - .When(x => AddSingletonDefinedAggregator()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) - .And(x => ThenTheAggregatorsAreSingleton()) - .BDDfy(); - } - - [Fact] - public void should_add_transient_defined_aggregators() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddTransientDefinedAggregator()) - .When(x => AddTransientDefinedAggregator()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) - .And(x => ThenTheAggregatorsAreTransient()) - .BDDfy(); - } - - private void AddSingletonDefinedAggregator() - where T : class, IDefinedAggregator - { - _ocelotBuilder.AddSingletonDefinedAggregator(); - } - - private void AddTransientDefinedAggregator() - where T : class, IDefinedAggregator - { - _ocelotBuilder.AddTransientDefinedAggregator(); - } - - private void ThenTheSpecificHandlersAreSingleton() - { - var handlers = _serviceProvider.GetServices().ToList(); - var first = handlers[0]; - handlers = _serviceProvider.GetServices().ToList(); - var second = handlers[0]; - first.ShouldBe(second); - } - - private void ThenTheSpecificHandlersAreTransient() - { - var handlers = _serviceProvider.GetServices().ToList(); - var first = handlers[0]; - handlers = _serviceProvider.GetServices().ToList(); - var second = handlers[0]; - first.ShouldNotBe(second); - } - - private void ThenTheGlobalHandlersAreSingleton() - { - var handlers = _serviceProvider.GetServices().ToList(); - var first = handlers[0].DelegatingHandler; - handlers = _serviceProvider.GetServices().ToList(); - var second = handlers[0].DelegatingHandler; - first.ShouldBe(second); - } - - private void ThenTheGlobalHandlersAreTransient() - { - var handlers = _serviceProvider.GetServices().ToList(); - var first = handlers[0].DelegatingHandler; - handlers = _serviceProvider.GetServices().ToList(); - var second = handlers[0].DelegatingHandler; - first.ShouldNotBe(second); - } - - private void WhenISetUpAdministration() - { - _ocelotBuilder.AddAdministration("/administration", "secret"); - } - - private void WhenISetUpAdministration(Action options) - { - _ocelotBuilder.AddAdministration("/administration", options); - } - - private void AddTransientGlobalDelegatingHandler() - where T : DelegatingHandler - { - _ocelotBuilder.AddTransientDelegatingHandler(true); - } - - private void AddSpecificTransientDelegatingHandler() - where T : DelegatingHandler - { - _ocelotBuilder.AddTransientDelegatingHandler(); - } - - private void ThenTheCorrectAdminPathIsRegitered() - { - _serviceProvider = _services.BuildServiceProvider(); - var path = _serviceProvider.GetService(); - path.Path.ShouldBe("/administration"); - } - - private void ThenTheProviderIsRegisteredAndReturnsHandlers() - { - _serviceProvider = _services.BuildServiceProvider(); - var handlers = _serviceProvider.GetServices().ToList(); - handlers[0].DelegatingHandler.ShouldBeOfType(); - handlers[1].DelegatingHandler.ShouldBeOfType(); - } - - private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers() - { - _serviceProvider = _services.BuildServiceProvider(); - var handlers = _serviceProvider.GetServices().ToList(); - handlers[0].ShouldBeOfType(); - handlers[1].ShouldBeOfType(); - } - - private void ThenTheProviderIsRegisteredAndReturnsSpecificAggregators() - { - _serviceProvider = _services.BuildServiceProvider(); - var handlers = _serviceProvider.GetServices().ToList(); - handlers[0].ShouldBeOfType(); - handlers[1].ShouldBeOfType(); - } - - private void ThenTheAggregatorsAreTransient() - { - var aggregators = _serviceProvider.GetServices().ToList(); - var first = aggregators[0]; - aggregators = _serviceProvider.GetServices().ToList(); - var second = aggregators[0]; - first.ShouldNotBe(second); - } - - private void ThenTheAggregatorsAreSingleton() - { - var aggregators = _serviceProvider.GetServices().ToList(); - var first = aggregators[0]; - aggregators = _serviceProvider.GetServices().ToList(); - var second = aggregators[0]; - first.ShouldBe(second); - } - - private void OnlyOneVersionOfEachCacheIsRegistered() - { - var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache)); - var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager)); - var instance = (ICacheManager)outputCacheManager.ImplementationInstance; - var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache)); - var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager)); - var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache)); - var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager)); - - instance.Configuration.MaxRetries.ShouldBe(_maxRetries); - outputCache.ShouldNotBeNull(); - ocelotConfigCache.ShouldNotBeNull(); - ocelotConfigCacheManager.ShouldNotBeNull(); - fileConfigCache.ShouldNotBeNull(); - fileConfigCacheManager.ShouldNotBeNull(); - } - - private void WhenISetUpConsul() - { - try - { - _ocelotBuilder.AddStoreOcelotConfigurationInConsul(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenISetUpRafty() - { - try - { - _ocelotBuilder.AddAdministration("/administration", "secret").AddRafty(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void AddGlobalDelegatingHandler() - where T : DelegatingHandler - { - _ocelotBuilder.AddSingletonDelegatingHandler(true); - } - - private void AddSpecificDelegatingHandler() - where T : DelegatingHandler - { - _ocelotBuilder.AddSingletonDelegatingHandler(); - } - - private void ThenAnOcelotBuilderIsReturned() - { - _ocelotBuilder.ShouldBeOfType(); - } - - private void WhenISetUpOcelotServices() - { - try - { - _ocelotBuilder = _services.AddOcelot(_configRoot); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenISetUpOcelotServicesWithoutConfig() - { - try - { - _ocelotBuilder = _services.AddOcelot(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenISetUpCacheManager() - { - try - { - _ocelotBuilder.AddCacheManager(x => { - x.WithMaxRetries(_maxRetries); - x.WithDictionaryHandle(); - }); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenISetUpOpentracing() - { - try - { - _ocelotBuilder.AddOpenTracing( - option => - { - option.CollectorUrl = "http://localhost:9618"; - option.Service = "Ocelot.ManualTest"; - } - ); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenIAccessLoggerFactory() - { - try - { - _serviceProvider = _services.BuildServiceProvider(); - var logger = _serviceProvider.GetService(); - logger.ShouldNotBeNull(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenIAccessOcelotHttpTracingHandler() - { - try - { - var tracingHandler = _serviceProvider.GetService(); - tracingHandler.ShouldNotBeNull(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenIValidateScopes() - { - try - { - _serviceProvider = _services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true }); - } - catch (Exception e) - { - _ex = e; - } - } - - private void ThenAnExceptionIsntThrown() - { - _ex.ShouldBeNull(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using CacheManager.Core; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Internal; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Ocelot.Cache; +using Ocelot.Configuration; +using Ocelot.Configuration.File; +using Ocelot.Configuration.Setter; +using Ocelot.DependencyInjection; +using Ocelot.Requester; +using Ocelot.UnitTests.Requester; +using Shouldly; +using IdentityServer4.AccessTokenValidation; +using TestStack.BDDfy; +using Xunit; +using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests; +using Ocelot.Middleware.Multiplexer; + +namespace Ocelot.UnitTests.DependencyInjection +{ + public class OcelotBuilderTests + { + private readonly IServiceCollection _services; + private IServiceProvider _serviceProvider; + private readonly IConfiguration _configRoot; + private IOcelotBuilder _ocelotBuilder; + private readonly int _maxRetries; + private Exception _ex; + + public OcelotBuilderTests() + { + _configRoot = new ConfigurationRoot(new List()); + _services = new ServiceCollection(); + _services.AddSingleton(); + _services.AddSingleton(_configRoot); + _maxRetries = 100; + } + + [Fact] + public void should_add_specific_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddSpecificTransientDelegatingHandler()) + .And(x => AddSpecificTransientDelegatingHandler()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) + .And(x => ThenTheSpecificHandlersAreTransient()) + .BDDfy(); + } + + [Fact] + public void should_add_global_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTransientGlobalDelegatingHandler()) + .And(x => AddTransientGlobalDelegatingHandler()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) + .And(x => ThenTheGlobalHandlersAreTransient()) + .BDDfy(); + } + + [Fact] + public void should_set_up_services() + { + this.When(x => WhenISetUpOcelotServices()) + .Then(x => ThenAnExceptionIsntThrown()) + .BDDfy(); + } + + [Fact] + public void should_return_ocelot_builder() + { + this.When(x => WhenISetUpOcelotServices()) + .Then(x => ThenAnOcelotBuilderIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_set_up_cache_manager() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpCacheManager()) + .Then(x => ThenAnExceptionIsntThrown()) + .And(x => OnlyOneVersionOfEachCacheIsRegistered()) + .BDDfy(); + } + + [Fact] + public void should_set_up_consul() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpConsul()) + .Then(x => ThenAnExceptionIsntThrown()) + .BDDfy(); + } + + [Fact] + public void should_set_up_rafty() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpRafty()) + .Then(x => ThenAnExceptionIsntThrown()) + .Then(x => ThenTheCorrectAdminPathIsRegitered()) + .BDDfy(); + } + + [Fact] + public void should_set_up_administration_with_identity_server_options() + { + Action options = o => {}; + + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpAdministration(options)) + .Then(x => ThenAnExceptionIsntThrown()) + .Then(x => ThenTheCorrectAdminPathIsRegitered()) + .BDDfy(); + } + + [Fact] + public void should_set_up_administration() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpAdministration()) + .Then(x => ThenAnExceptionIsntThrown()) + .Then(x => ThenTheCorrectAdminPathIsRegitered()) + .BDDfy(); + } + + [Fact] + public void should_use_logger_factory() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenIValidateScopes()) + .When(x => WhenIAccessLoggerFactory()) + .Then(x => ThenAnExceptionIsntThrown()) + .BDDfy(); + } + + [Fact] + public void should_set_up_tracing() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpOpentracing()) + .When(x => WhenIAccessOcelotHttpTracingHandler()) + .BDDfy(); + } + + [Fact] + public void should_set_up_without_passing_in_config() + { + this.When(x => WhenISetUpOcelotServicesWithoutConfig()) + .Then(x => ThenAnExceptionIsntThrown()) + .BDDfy(); + } + + [Fact] + public void should_add_singleton_defined_aggregators() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddSingletonDefinedAggregator()) + .When(x => AddSingletonDefinedAggregator()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) + .And(x => ThenTheAggregatorsAreSingleton()) + .BDDfy(); + } + + [Fact] + public void should_add_transient_defined_aggregators() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTransientDefinedAggregator()) + .When(x => AddTransientDefinedAggregator()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) + .And(x => ThenTheAggregatorsAreTransient()) + .BDDfy(); + } + + private void AddSingletonDefinedAggregator() + where T : class, IDefinedAggregator + { + _ocelotBuilder.AddSingletonDefinedAggregator(); + } + + private void AddTransientDefinedAggregator() + where T : class, IDefinedAggregator + { + _ocelotBuilder.AddTransientDefinedAggregator(); + } + + private void ThenTheSpecificHandlersAreSingleton() + { + var handlers = _serviceProvider.GetServices().ToList(); + var first = handlers[0]; + handlers = _serviceProvider.GetServices().ToList(); + var second = handlers[0]; + first.ShouldBe(second); + } + + private void ThenTheSpecificHandlersAreTransient() + { + var handlers = _serviceProvider.GetServices().ToList(); + var first = handlers[0]; + handlers = _serviceProvider.GetServices().ToList(); + var second = handlers[0]; + first.ShouldNotBe(second); + } + + private void ThenTheGlobalHandlersAreTransient() + { + var handlers = _serviceProvider.GetServices().ToList(); + var first = handlers[0].DelegatingHandler; + handlers = _serviceProvider.GetServices().ToList(); + var second = handlers[0].DelegatingHandler; + first.ShouldNotBe(second); + } + + private void WhenISetUpAdministration() + { + _ocelotBuilder.AddAdministration("/administration", "secret"); + } + + private void WhenISetUpAdministration(Action options) + { + _ocelotBuilder.AddAdministration("/administration", options); + } + + private void AddTransientGlobalDelegatingHandler() + where T : DelegatingHandler + { + _ocelotBuilder.AddDelegatingHandler(true); + } + + private void AddSpecificTransientDelegatingHandler() + where T : DelegatingHandler + { + _ocelotBuilder.AddDelegatingHandler(); + } + + private void ThenTheCorrectAdminPathIsRegitered() + { + _serviceProvider = _services.BuildServiceProvider(); + var path = _serviceProvider.GetService(); + path.Path.ShouldBe("/administration"); + } + + private void ThenTheProviderIsRegisteredAndReturnsHandlers() + { + _serviceProvider = _services.BuildServiceProvider(); + var handlers = _serviceProvider.GetServices().ToList(); + handlers[0].DelegatingHandler.ShouldBeOfType(); + handlers[1].DelegatingHandler.ShouldBeOfType(); + } + + private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers() + { + _serviceProvider = _services.BuildServiceProvider(); + var handlers = _serviceProvider.GetServices().ToList(); + handlers[0].ShouldBeOfType(); + handlers[1].ShouldBeOfType(); + } + + private void ThenTheProviderIsRegisteredAndReturnsSpecificAggregators() + { + _serviceProvider = _services.BuildServiceProvider(); + var handlers = _serviceProvider.GetServices().ToList(); + handlers[0].ShouldBeOfType(); + handlers[1].ShouldBeOfType(); + } + + private void ThenTheAggregatorsAreTransient() + { + var aggregators = _serviceProvider.GetServices().ToList(); + var first = aggregators[0]; + aggregators = _serviceProvider.GetServices().ToList(); + var second = aggregators[0]; + first.ShouldNotBe(second); + } + + private void ThenTheAggregatorsAreSingleton() + { + var aggregators = _serviceProvider.GetServices().ToList(); + var first = aggregators[0]; + aggregators = _serviceProvider.GetServices().ToList(); + var second = aggregators[0]; + first.ShouldBe(second); + } + + private void OnlyOneVersionOfEachCacheIsRegistered() + { + var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache)); + var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager)); + var instance = (ICacheManager)outputCacheManager.ImplementationInstance; + var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache)); + var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager)); + var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache)); + var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager)); + + instance.Configuration.MaxRetries.ShouldBe(_maxRetries); + outputCache.ShouldNotBeNull(); + ocelotConfigCache.ShouldNotBeNull(); + ocelotConfigCacheManager.ShouldNotBeNull(); + fileConfigCache.ShouldNotBeNull(); + fileConfigCacheManager.ShouldNotBeNull(); + } + + private void WhenISetUpConsul() + { + try + { + _ocelotBuilder.AddStoreOcelotConfigurationInConsul(); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenISetUpRafty() + { + try + { + _ocelotBuilder.AddAdministration("/administration", "secret").AddRafty(); + } + catch (Exception e) + { + _ex = e; + } + } + + private void AddGlobalDelegatingHandler() + where T : DelegatingHandler + { + _ocelotBuilder.AddDelegatingHandler(true); + } + + private void AddSpecificDelegatingHandler() + where T : DelegatingHandler + { + _ocelotBuilder.AddDelegatingHandler(); + } + + private void ThenAnOcelotBuilderIsReturned() + { + _ocelotBuilder.ShouldBeOfType(); + } + + private void WhenISetUpOcelotServices() + { + try + { + _ocelotBuilder = _services.AddOcelot(_configRoot); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenISetUpOcelotServicesWithoutConfig() + { + try + { + _ocelotBuilder = _services.AddOcelot(); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenISetUpCacheManager() + { + try + { + _ocelotBuilder.AddCacheManager(x => { + x.WithMaxRetries(_maxRetries); + x.WithDictionaryHandle(); + }); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenISetUpOpentracing() + { + try + { + _ocelotBuilder.AddOpenTracing( + option => + { + option.CollectorUrl = "http://localhost:9618"; + option.Service = "Ocelot.ManualTest"; + } + ); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenIAccessLoggerFactory() + { + try + { + _serviceProvider = _services.BuildServiceProvider(); + var logger = _serviceProvider.GetService(); + logger.ShouldNotBeNull(); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenIAccessOcelotHttpTracingHandler() + { + try + { + var tracingHandler = _serviceProvider.GetService(); + tracingHandler.ShouldNotBeNull(); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenIValidateScopes() + { + try + { + _serviceProvider = _services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true }); + } + catch (Exception e) + { + _ex = e; + } + } + + private void ThenAnExceptionIsntThrown() + { + _ex.ShouldBeNull(); + } + } +}