mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:42:50 +08:00
Add ConfigAwarePlaceholders class (#997)
* Add ConfigAwarePlaceholders class This allows placeholder values to be sourced from IConfiguration in addition to set values. * Rework how IPlaceholders is decorated in container
This commit is contained in:
parent
b6f3f0f28a
commit
bef40041ba
@ -24,5 +24,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
IOcelotBuilder AddTransientDefinedAggregator<T>()
|
IOcelotBuilder AddTransientDefinedAggregator<T>()
|
||||||
where T : class, IDefinedAggregator;
|
where T : class, IDefinedAggregator;
|
||||||
|
|
||||||
|
IOcelotBuilder AddConfigPlaceholders();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
using Ocelot.Security.IPSecurity;
|
using Ocelot.Security.IPSecurity;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@ -209,5 +210,39 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOcelotBuilder AddConfigPlaceholders()
|
||||||
|
{
|
||||||
|
// see: https://greatrexpectations.com/2018/10/25/decorators-in-net-core-with-dependency-injection
|
||||||
|
var wrappedDescriptor = Services.First(x => x.ServiceType == typeof(IPlaceholders));
|
||||||
|
|
||||||
|
var objectFactory = ActivatorUtilities.CreateFactory(
|
||||||
|
typeof(ConfigAwarePlaceholders),
|
||||||
|
new[] { typeof(IPlaceholders) });
|
||||||
|
|
||||||
|
Services.Replace(ServiceDescriptor.Describe(
|
||||||
|
typeof(IPlaceholders),
|
||||||
|
s => (IPlaceholders) objectFactory(s,
|
||||||
|
new[] {CreateInstance(s, wrappedDescriptor)}),
|
||||||
|
wrappedDescriptor.Lifetime
|
||||||
|
));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object CreateInstance(IServiceProvider services, ServiceDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor.ImplementationInstance != null)
|
||||||
|
{
|
||||||
|
return descriptor.ImplementationInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.ImplementationFactory != null)
|
||||||
|
{
|
||||||
|
return descriptor.ImplementationFactory(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
src/Ocelot/Infrastructure/ConfigAwarePlaceholders.cs
Normal file
61
src/Ocelot/Infrastructure/ConfigAwarePlaceholders.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Request.Middleware;
|
||||||
|
using Responses;
|
||||||
|
|
||||||
|
public class ConfigAwarePlaceholders : IPlaceholders
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly IPlaceholders _placeholders;
|
||||||
|
|
||||||
|
public ConfigAwarePlaceholders(IConfiguration configuration, IPlaceholders placeholders)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_placeholders = placeholders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<string> Get(string key)
|
||||||
|
{
|
||||||
|
var placeholderResponse = _placeholders.Get(key);
|
||||||
|
|
||||||
|
if (!placeholderResponse.IsError)
|
||||||
|
{
|
||||||
|
return placeholderResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetFromConfig(CleanKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<string> Get(string key, DownstreamRequest request)
|
||||||
|
{
|
||||||
|
var placeholderResponse = _placeholders.Get(key, request);
|
||||||
|
|
||||||
|
if (!placeholderResponse.IsError)
|
||||||
|
{
|
||||||
|
return placeholderResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetFromConfig(CleanKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response Add(string key, Func<Response<string>> func)
|
||||||
|
=> _placeholders.Add(key, func);
|
||||||
|
|
||||||
|
public Response Remove(string key)
|
||||||
|
=> _placeholders.Remove(key);
|
||||||
|
|
||||||
|
private string CleanKey(string key)
|
||||||
|
=> Regex.Replace(key, @"[{}]", string.Empty, RegexOptions.None);
|
||||||
|
|
||||||
|
private Response<string> GetFromConfig(string key)
|
||||||
|
{
|
||||||
|
var valueFromConfig = _configuration[key];
|
||||||
|
return valueFromConfig == null
|
||||||
|
? (Response<string>) new ErrorResponse<string>(new CouldNotFindPlaceholderError(key))
|
||||||
|
: new OkResponse<string>(valueFromConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests;
|
using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests;
|
||||||
@ -136,6 +137,16 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_replace_iplaceholder()
|
||||||
|
{
|
||||||
|
this.Given(x => x.WhenISetUpOcelotServices())
|
||||||
|
.When(x => AddConfigPlaceholders())
|
||||||
|
.Then(x => ThenAnExceptionIsntThrown())
|
||||||
|
.And(x => ThenTheIPlaceholderInstanceIsReplaced())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
private void AddSingletonDefinedAggregator<T>()
|
private void AddSingletonDefinedAggregator<T>()
|
||||||
where T : class, IDefinedAggregator
|
where T : class, IDefinedAggregator
|
||||||
{
|
{
|
||||||
@ -148,6 +159,11 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
_ocelotBuilder.AddTransientDefinedAggregator<T>();
|
_ocelotBuilder.AddTransientDefinedAggregator<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddConfigPlaceholders()
|
||||||
|
{
|
||||||
|
_ocelotBuilder.AddConfigPlaceholders();
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenTheSpecificHandlersAreTransient()
|
private void ThenTheSpecificHandlersAreTransient()
|
||||||
{
|
{
|
||||||
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
|
||||||
@ -235,6 +251,13 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheIPlaceholderInstanceIsReplaced()
|
||||||
|
{
|
||||||
|
_serviceProvider = _services.BuildServiceProvider();
|
||||||
|
var placeholders = _serviceProvider.GetService<IPlaceholders>();
|
||||||
|
placeholders.ShouldBeOfType<ConfigAwarePlaceholders>();
|
||||||
|
}
|
||||||
|
|
||||||
private void WhenISetUpOcelotServices()
|
private void WhenISetUpOcelotServices()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
namespace Ocelot.UnitTests.Infrastructure
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class ConfigAwarePlaceholdersTests
|
||||||
|
{
|
||||||
|
private readonly IPlaceholders _placeholders;
|
||||||
|
private readonly Mock<IPlaceholders> _basePlaceholders;
|
||||||
|
|
||||||
|
public ConfigAwarePlaceholdersTests()
|
||||||
|
{
|
||||||
|
var configurationBuilder = new ConfigurationBuilder();
|
||||||
|
configurationBuilder.AddJsonFile("appsettings.json");
|
||||||
|
var configuration = configurationBuilder.Build();
|
||||||
|
|
||||||
|
_basePlaceholders = new Mock<IPlaceholders>();
|
||||||
|
_placeholders = new ConfigAwarePlaceholders(configuration, _basePlaceholders.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_value_from_underlying_placeholders()
|
||||||
|
{
|
||||||
|
var baseUrl = "http://www.bbc.co.uk";
|
||||||
|
const string key = "{BaseUrl}";
|
||||||
|
|
||||||
|
_basePlaceholders.Setup(x => x.Get(key)).Returns(new OkResponse<string>(baseUrl));
|
||||||
|
var result = _placeholders.Get(key);
|
||||||
|
result.Data.ShouldBe(baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_value_from_config_with_same_name_as_placeholder_if_underlying_placeholder_not_found()
|
||||||
|
{
|
||||||
|
const string expected = "http://foo-bar.co.uk";
|
||||||
|
var baseUrl = "http://www.bbc.co.uk";
|
||||||
|
const string key = "{BaseUrl}";
|
||||||
|
|
||||||
|
_basePlaceholders.Setup(x => x.Get(key)).Returns(new ErrorResponse<string>(new FakeError()));
|
||||||
|
var result = _placeholders.Get(key);
|
||||||
|
result.Data.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("{TestConfig}")]
|
||||||
|
[InlineData("{TestConfigNested:Child}")]
|
||||||
|
public void should_return_value_from_config(string key)
|
||||||
|
{
|
||||||
|
const string expected = "foo";
|
||||||
|
|
||||||
|
_basePlaceholders.Setup(x => x.Get(key)).Returns(new ErrorResponse<string>(new FakeError()));
|
||||||
|
var result = _placeholders.Get(key);
|
||||||
|
result.Data.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_underyling_when_added()
|
||||||
|
{
|
||||||
|
const string key = "{Test}";
|
||||||
|
Func<Response<string>> func = () => new OkResponse<string>("test)");
|
||||||
|
_placeholders.Add(key, func);
|
||||||
|
_basePlaceholders.Verify(p => p.Add(key, func), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_underyling_when_removed()
|
||||||
|
{
|
||||||
|
const string key = "{Test}";
|
||||||
|
_placeholders.Remove(key);
|
||||||
|
_basePlaceholders.Verify(p => p.Remove(key), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,5 +20,10 @@
|
|||||||
"port": 5000,
|
"port": 5000,
|
||||||
"hostName": "localhost"
|
"hostName": "localhost"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"BaseUrl": "http://foo-bar.co.uk",
|
||||||
|
"TestConfig": "foo",
|
||||||
|
"TestConfigNested":{
|
||||||
|
"Child": "foo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user