method to match urls and template urls

This commit is contained in:
Tom Gardham-Pallister
2016-07-08 19:33:22 +01:00
parent 7332da7230
commit 4f2d94ceba
21 changed files with 718 additions and 96 deletions

View File

@ -1,5 +1,5 @@
using Ocelot.Library.Infrastructure.Responses;
using Ocelot.Library.Infrastructure.Router;
using Ocelot.Library.Infrastructure.Router.UpstreamRouter;
using Shouldly;
using Xunit;
@ -9,13 +9,13 @@ namespace Ocelot.UnitTests
{
private string _upstreamApiUrl;
private string _apiKey;
private IRouterService _router;
private IUpstreamRouter _router;
private Response _response;
private Response<Route> _getRouteResponse;
public RouterTests()
public RouterTests()
{
_router = new InMemoryRouterService();
_router = new InMemoryUpstreamRouter();
}
[Fact]
@ -34,7 +34,7 @@ namespace Ocelot.UnitTests
WhenIRetrieveTheRouteByKey();
ThenTheRouteIsReturned();
}
[Fact]
public void should_return_error_response_when_key_already_used()
{
@ -77,8 +77,8 @@ namespace Ocelot.UnitTests
private void ThenTheRouteIsReturned()
{
_getRouteResponse.Data.ApiKey.ShouldBe(_apiKey);
_getRouteResponse.Data.UpstreamRoute.ShouldBe(_upstreamApiUrl);
_getRouteResponse.Data.DownstreamUrl.ShouldBe(_apiKey);
_getRouteResponse.Data.UpstreamUrl.ShouldBe(_upstreamApiUrl);
}
private void GivenIHaveSetUpAnApiKeyAndUpstreamUrl(string apiKey, string upstreamUrl)

View File

@ -0,0 +1,133 @@
using System;
using Ocelot.Library.Infrastructure.Responses;
using Ocelot.Library.Infrastructure.Router.UpstreamRouter;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests
{
public class UrlMapperTests
{
private UrlToUrlTemplateMatcher _urlMapper;
public UrlMapperTests()
{
_urlMapper = new UrlToUrlTemplateMatcher();
}
[Fact]
public void can_match_down_stream_url_with_downstream_template_with_one_query_string_parameter()
{
var downstreamUrl = "api/product/products/?soldout=false";
var downstreamTemplate = "api/product/products/";
var result = _urlMapper.Match(downstreamUrl, downstreamTemplate);
result.ShouldBeTrue();
}
[Fact]
public void can_match_down_stream_url_with_downstream_template_with_one_query_string_parameter_and_one_template()
{
var downstreamUrl = "api/product/products/1/variants/?soldout=false";
var downstreamTemplate = "api/product/products/{productId}/variants/";
var result = _urlMapper.Match(downstreamUrl, downstreamTemplate);
result.ShouldBeTrue();
}
[Fact]
public void can_match_down_stream_url_with_downstream_template_with_one_place_holder()
{
var downstreamUrl = "api/product/products/1";
var downstreamTemplate = "api/product/products/{productId}";
var result = _urlMapper.Match(downstreamUrl, downstreamTemplate);
result.ShouldBeTrue();
}
[Fact]
public void can_match_down_stream_url_with_downstream_template_with_two_place_holders()
{
var downstreamUrl = "api/product/products/1/2";
var downstreamTemplate = "api/product/products/{productId}/{categoryId}";
var result = _urlMapper.Match(downstreamUrl, downstreamTemplate);
result.ShouldBeTrue();
}
[Fact]
public void can_match_down_stream_url_with_downstream_template_with_two_place_holders_seperated_by_something()
{
var downstreamUrl = "api/product/products/1/categories/2";
var downstreamTemplate = "api/product/products/{productId}/categories/{categoryId}";
var result = _urlMapper.Match(downstreamUrl, downstreamTemplate);
result.ShouldBeTrue();
}
[Fact]
public void can_match_down_stream_url_with_downstream_template_with_three_place_holders_seperated_by_something()
{
var downstreamUrl = "api/product/products/1/categories/2/variant/123";
var downstreamTemplate = "api/product/products/{productId}/categories/{categoryId}/variant/{variantId}";
var result = _urlMapper.Match(downstreamUrl, downstreamTemplate);
result.ShouldBeTrue();
}
[Fact]
public void can_match_down_stream_url_with_downstream_template_with_three_place_holders()
{
var downstreamUrl = "api/product/products/1/categories/2/variant/";
var downstreamTemplate = "api/product/products/{productId}/categories/{categoryId}/variant/";
var result = _urlMapper.Match(downstreamUrl, downstreamTemplate);
result.ShouldBeTrue();
}
}
public class UrlToUrlTemplateMatcher
{
public bool Match(string url, string urlTemplate)
{
url = url.ToLower();
urlTemplate = urlTemplate.ToLower();
int counterForUrl = 0;
for (int counterForTemplate = 0; counterForTemplate < urlTemplate.Length; counterForTemplate++)
{
if (CharactersDontMatch(urlTemplate[counterForTemplate], url[counterForUrl]) && ContinueScanningUrl(counterForUrl,url.Length))
{
if (IsPlaceholder(urlTemplate[counterForTemplate]))
{
counterForTemplate = GetNextCounterPosition(urlTemplate, counterForTemplate, '}');
counterForUrl = GetNextCounterPosition(url, counterForUrl, '/');
continue;
}
else
{
return false;
}
}
counterForUrl++;
}
return true;
}
private int GetNextCounterPosition(string urlTemplate, int counterForTemplate, char delimiter)
{
var closingPlaceHolderPositionOnTemplate = urlTemplate.IndexOf(delimiter, counterForTemplate);
return closingPlaceHolderPositionOnTemplate + 1;
}
private bool CharactersDontMatch(char characterOne, char characterTwo)
{
return characterOne != characterTwo;
}
private bool ContinueScanningUrl(int counterForUrl, int urlLength)
{
return counterForUrl < urlLength;
}
private bool IsPlaceholder(char character)
{
return character == '{';
}
}
}

View File

@ -0,0 +1,111 @@
using Ocelot.Library.Infrastructure.Responses;
using Ocelot.Library.Infrastructure.Router.UrlPathRouter;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests
{
public class UrlPathRouterTests
{
private string _upstreamUrlPath;
private string _downstreamUrlPath;
private IUrlPathRouter _router;
private Response _response;
private Response<UrlPath> _getResponse;
public UrlPathRouterTests()
{
_router = new InMemoryUrlPathRouter();
}
[Fact]
public void can_add_url_path()
{
GivenIHaveAnUpstreamUrlPath("api/products/products/{productId}");
GivenIWantToRouteRequestsToMyUpstreamUrlPath("api/products/{productId}");
WhenIAddTheConfiguration();
ThenTheResponseIsSuccesful();
}
[Fact]
public void can_get_url_path()
{
GivenIHaveSetUpADownstreamUrlPathAndAnUpstreamUrlPath("api2", "http://www.someapi.com/api2");
WhenIRetrieveTheUrlPathByDownstreamUrl();
ThenTheUrlPathIsReturned();
}
[Fact]
public void should_return_error_response_when_url_path_already_used()
{
GivenIHaveSetUpADownstreamUrlPathAndAnUpstreamUrlPath("api2", "http://www.someapi.com/api2");
WhenITryToUseTheSameDownstreamUrl();
ThenTheDownstreamUrlAlreadyBeenUsed();
}
[Fact]
public void should_return_error_response_if_key_doesnt_exist()
{
GivenIWantToRouteRequestsToMyUpstreamUrlPath("api");
WhenIRetrieveTheUrlPathByDownstreamUrl();
ThenTheKeyDoesNotExist();
}
private void WhenITryToUseTheSameDownstreamUrl()
{
WhenIAddTheConfiguration();
}
private void ThenTheDownstreamUrlAlreadyBeenUsed()
{
_response.ShouldNotBeNull();
_response.ShouldBeOfType<ErrorResponse>();
_response.Errors[0].Message.ShouldBe("This key has already been used");
}
private void ThenTheKeyDoesNotExist()
{
_getResponse.ShouldNotBeNull();
_getResponse.ShouldBeOfType<ErrorResponse<UrlPath>>();
_getResponse.Errors[0].Message.ShouldBe("This key does not exist");
}
private void WhenIRetrieveTheUrlPathByDownstreamUrl()
{
_getResponse = _router.GetRoute(_downstreamUrlPath);
}
private void ThenTheUrlPathIsReturned()
{
_getResponse.Data.DownstreamUrlPathTemplate.ShouldBe(_downstreamUrlPath);
_getResponse.Data.UpstreamUrlPathTemplate.ShouldBe(_upstreamUrlPath);
}
private void GivenIHaveSetUpADownstreamUrlPathAndAnUpstreamUrlPath(string downstream, string upstreamApiUrl)
{
GivenIHaveAnUpstreamUrlPath(upstreamApiUrl);
GivenIWantToRouteRequestsToMyUpstreamUrlPath(downstream);
WhenIAddTheConfiguration();
}
private void GivenIHaveAnUpstreamUrlPath(string upstreamApiUrl)
{
_upstreamUrlPath = upstreamApiUrl;
}
private void GivenIWantToRouteRequestsToMyUpstreamUrlPath(string apiKey)
{
_downstreamUrlPath = apiKey;
}
private void WhenIAddTheConfiguration()
{
_response = _router.AddRoute(_downstreamUrlPath, _upstreamUrlPath);
}
private void ThenTheResponseIsSuccesful()
{
_response.ShouldBeOfType<OkResponse>();
}
}
}