From 4f2d94ceba0ddc71c4754fda28e46100f9cc1ab5 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Fri, 8 Jul 2016 19:33:22 +0100 Subject: [PATCH] method to match urls and template urls --- .../Infrastructure/Router/IRouterService.cs | 10 - .../Router/InMemoryRouterService.cs | 41 --- .../Infrastructure/Router/Route.cs | 14 -- .../Router/RouteKeyAlreadyExists.cs | 12 - .../Router/RouteKeyDoesNotExist.cs | 12 - .../Router/UpstreamRouter/IUpstreamRouter.cs | 10 + .../UpstreamRouter/InMemoryUpstreamRouter.cs | 38 +++ .../Router/UpstreamRouter/Route.cs | 14 ++ .../UpstreamRouter/RouteKeyAlreadyExists.cs | 12 + .../UpstreamRouter/RouteKeyDoesNotExist.cs | 12 + .../DownstreamUrlPathAlreadyExists.cs | 12 + .../DownstreamUrlPathDoesNotExist.cs | 12 + .../Router/UrlPathRouter/IUrlPathRouter.cs | 10 + .../UrlPathRouter/InMemoryUrlPathRouter.cs | 38 +++ .../Router/UrlPathRouter/UrlPath.cs | 14 ++ test/Ocelot.AcceptanceTests/.gitignore | 234 ++++++++++++++++++ test/Ocelot.AcceptanceTests/RouterTests.cs | 24 ++ test/Ocelot.AcceptanceTests/project.json | 37 +++ ...{RouterTests.cs => UpstreamRouterTests.cs} | 14 +- test/Ocelot.UnitTests/UrlMapperTests.cs | 133 ++++++++++ test/Ocelot.UnitTests/UrlPathRouterTests.cs | 111 +++++++++ 21 files changed, 718 insertions(+), 96 deletions(-) delete mode 100644 src/Ocelot.Library/Infrastructure/Router/IRouterService.cs delete mode 100644 src/Ocelot.Library/Infrastructure/Router/InMemoryRouterService.cs delete mode 100644 src/Ocelot.Library/Infrastructure/Router/Route.cs delete mode 100644 src/Ocelot.Library/Infrastructure/Router/RouteKeyAlreadyExists.cs delete mode 100644 src/Ocelot.Library/Infrastructure/Router/RouteKeyDoesNotExist.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/IUpstreamRouter.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/InMemoryUpstreamRouter.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/Route.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyAlreadyExists.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyDoesNotExist.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathAlreadyExists.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathDoesNotExist.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/IUrlPathRouter.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/InMemoryUrlPathRouter.cs create mode 100644 src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/UrlPath.cs create mode 100644 test/Ocelot.AcceptanceTests/.gitignore create mode 100644 test/Ocelot.AcceptanceTests/RouterTests.cs create mode 100644 test/Ocelot.AcceptanceTests/project.json rename test/Ocelot.UnitTests/{RouterTests.cs => UpstreamRouterTests.cs} (90%) create mode 100644 test/Ocelot.UnitTests/UrlMapperTests.cs create mode 100644 test/Ocelot.UnitTests/UrlPathRouterTests.cs diff --git a/src/Ocelot.Library/Infrastructure/Router/IRouterService.cs b/src/Ocelot.Library/Infrastructure/Router/IRouterService.cs deleted file mode 100644 index 61f7b48d..00000000 --- a/src/Ocelot.Library/Infrastructure/Router/IRouterService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Ocelot.Library.Infrastructure.Responses; - -namespace Ocelot.Library.Infrastructure.Router -{ - public interface IRouterService - { - Response AddRoute(string apiKey, string upstreamApiBaseUrl); - Response GetRoute(string apiKey); - } -} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/InMemoryRouterService.cs b/src/Ocelot.Library/Infrastructure/Router/InMemoryRouterService.cs deleted file mode 100644 index d3e73746..00000000 --- a/src/Ocelot.Library/Infrastructure/Router/InMemoryRouterService.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using Ocelot.Library.Infrastructure.Responses; - -namespace Ocelot.Library.Infrastructure.Router -{ - public class InMemoryRouterService : IRouterService - { - private readonly Dictionary _routes; - public InMemoryRouterService() - { - _routes = new Dictionary(); - } - public Response AddRoute(string apiKey, string upstreamApiBaseUrl) - { - if(_routes.ContainsKey(apiKey)) - { - return new ErrorResponse(new List(){new RouteKeyAlreadyExists("This key has already been used")}); - } - - _routes.Add(apiKey, upstreamApiBaseUrl); - - return new OkResponse(); - } - - public Response GetRoute(string apiKey) - { - Console.WriteLine("looking for {0}", apiKey); - string upstreamApiBaseUrl = null; - - if(_routes.TryGetValue(apiKey, out upstreamApiBaseUrl)) - { - return new OkResponse(new Route(apiKey, upstreamApiBaseUrl)); - } - - Console.WriteLine("Couldnt find it"); - - return new ErrorResponse(new List(){new RouteKeyDoesNotExist("This key does not exist")}); - } - } -} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/Route.cs b/src/Ocelot.Library/Infrastructure/Router/Route.cs deleted file mode 100644 index f76f6173..00000000 --- a/src/Ocelot.Library/Infrastructure/Router/Route.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ocelot.Library.Infrastructure.Router -{ - public class Route - { - public Route(string apiKey, string upstreamRoute) - { - ApiKey = apiKey; - UpstreamRoute = upstreamRoute; - } - - public string ApiKey {get;private set;} - public string UpstreamRoute {get;private set;} - } -} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/RouteKeyAlreadyExists.cs b/src/Ocelot.Library/Infrastructure/Router/RouteKeyAlreadyExists.cs deleted file mode 100644 index 1ae13795..00000000 --- a/src/Ocelot.Library/Infrastructure/Router/RouteKeyAlreadyExists.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ocelot.Library.Infrastructure.Responses; - -namespace Ocelot.Library.Infrastructure.Router -{ - public class RouteKeyAlreadyExists : Error - { - public RouteKeyAlreadyExists(string message) - : base(message) - { - } - } -} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/RouteKeyDoesNotExist.cs b/src/Ocelot.Library/Infrastructure/Router/RouteKeyDoesNotExist.cs deleted file mode 100644 index f38aea5e..00000000 --- a/src/Ocelot.Library/Infrastructure/Router/RouteKeyDoesNotExist.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ocelot.Library.Infrastructure.Responses; - -namespace Ocelot.Library.Infrastructure.Router -{ - public class RouteKeyDoesNotExist : Error - { - public RouteKeyDoesNotExist(string message) - : base(message) - { - } - } -} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/IUpstreamRouter.cs b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/IUpstreamRouter.cs new file mode 100644 index 00000000..25323b8b --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/IUpstreamRouter.cs @@ -0,0 +1,10 @@ +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UpstreamRouter +{ + public interface IUpstreamRouter + { + Response AddRoute(string downstreamUrl, string upstreamUrl); + Response GetRoute(string downstreamUrl); + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/InMemoryUpstreamRouter.cs b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/InMemoryUpstreamRouter.cs new file mode 100644 index 00000000..7767dc41 --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/InMemoryUpstreamRouter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UpstreamRouter +{ + public class InMemoryUpstreamRouter : IUpstreamRouter + { + private readonly Dictionary _routes; + public InMemoryUpstreamRouter() + { + _routes = new Dictionary(); + } + public Response AddRoute(string downstreamUrl, string upstreamUrl) + { + if(_routes.ContainsKey(downstreamUrl)) + { + return new ErrorResponse(new List(){new RouteKeyAlreadyExists()}); + } + + _routes.Add(downstreamUrl, upstreamUrl); + + return new OkResponse(); + } + + public Response GetRoute(string downstreamUrl) + { + string upstreamUrl = null; + + if(_routes.TryGetValue(downstreamUrl, out upstreamUrl)) + { + return new OkResponse(new Route(downstreamUrl, upstreamUrl)); + } + + return new ErrorResponse(new List(){new RouteKeyDoesNotExist()}); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/Route.cs b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/Route.cs new file mode 100644 index 00000000..6b14d307 --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/Route.cs @@ -0,0 +1,14 @@ +namespace Ocelot.Library.Infrastructure.Router.UpstreamRouter +{ + public class Route + { + public Route(string downstreamUrl, string upstreamUrl) + { + DownstreamUrl = downstreamUrl; + UpstreamUrl = upstreamUrl; + } + + public string DownstreamUrl {get;private set;} + public string UpstreamUrl {get;private set;} + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyAlreadyExists.cs b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyAlreadyExists.cs new file mode 100644 index 00000000..19a436cc --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyAlreadyExists.cs @@ -0,0 +1,12 @@ +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UpstreamRouter +{ + public class RouteKeyAlreadyExists : Error + { + public RouteKeyAlreadyExists() + : base("This key has already been used") + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyDoesNotExist.cs b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyDoesNotExist.cs new file mode 100644 index 00000000..fe4c90ba --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UpstreamRouter/RouteKeyDoesNotExist.cs @@ -0,0 +1,12 @@ +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UpstreamRouter +{ + public class RouteKeyDoesNotExist : Error + { + public RouteKeyDoesNotExist() + : base("This key does not exist") + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathAlreadyExists.cs b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathAlreadyExists.cs new file mode 100644 index 00000000..3dc099a7 --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathAlreadyExists.cs @@ -0,0 +1,12 @@ +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UrlPathRouter +{ + public class DownstreamUrlPathTemplateAlreadyExists : Error + { + public DownstreamUrlPathTemplateAlreadyExists() + : base("This key has already been used") + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathDoesNotExist.cs b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathDoesNotExist.cs new file mode 100644 index 00000000..4c05e3d2 --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/DownstreamUrlPathDoesNotExist.cs @@ -0,0 +1,12 @@ +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UrlPathRouter +{ + public class DownstreamUrlPathTemplateDoesNotExist : Error + { + public DownstreamUrlPathTemplateDoesNotExist() + : base("This key does not exist") + { + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/IUrlPathRouter.cs b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/IUrlPathRouter.cs new file mode 100644 index 00000000..c39be36b --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/IUrlPathRouter.cs @@ -0,0 +1,10 @@ +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UrlPathRouter +{ + public interface IUrlPathRouter + { + Response AddRoute(string downstreamUrlPathTemplate, string upstreamUrlPathTemplate); + Response GetRoute(string downstreamUrlPathTemplate); + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/InMemoryUrlPathRouter.cs b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/InMemoryUrlPathRouter.cs new file mode 100644 index 00000000..73c358f2 --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/InMemoryUrlPathRouter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Ocelot.Library.Infrastructure.Responses; + +namespace Ocelot.Library.Infrastructure.Router.UrlPathRouter +{ + public class InMemoryUrlPathRouter : IUrlPathRouter + { + private readonly Dictionary _routes; + public InMemoryUrlPathRouter() + { + _routes = new Dictionary(); + } + public Response AddRoute(string downstreamUrlPathTemplate, string upstreamUrlPathTemplate) + { + if(_routes.ContainsKey(downstreamUrlPathTemplate)) + { + return new ErrorResponse(new List(){new DownstreamUrlPathTemplateAlreadyExists()}); + } + + _routes.Add(downstreamUrlPathTemplate, upstreamUrlPathTemplate); + + return new OkResponse(); + } + + public Response GetRoute(string downstreamUrlPathTemplate) + { + string upstreamUrlPathTemplate = null; + + if(_routes.TryGetValue(downstreamUrlPathTemplate, out upstreamUrlPathTemplate)) + { + return new OkResponse(new UrlPath(downstreamUrlPathTemplate, upstreamUrlPathTemplate)); + } + + return new ErrorResponse(new List(){new DownstreamUrlPathTemplateDoesNotExist()}); + } + } +} \ No newline at end of file diff --git a/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/UrlPath.cs b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/UrlPath.cs new file mode 100644 index 00000000..b1ea65aa --- /dev/null +++ b/src/Ocelot.Library/Infrastructure/Router/UrlPathRouter/UrlPath.cs @@ -0,0 +1,14 @@ +namespace Ocelot.Library.Infrastructure.Router.UrlPathRouter +{ + public class UrlPath + { + public UrlPath(string downstreamUrlPathTemplate, string upstreamUrlPathTemplate) + { + DownstreamUrlPathTemplate = downstreamUrlPathTemplate; + UpstreamUrlPathTemplate = upstreamUrlPathTemplate; + } + + public string DownstreamUrlPathTemplate {get;private set;} + public string UpstreamUrlPathTemplate {get;private set;} + } +} \ No newline at end of file diff --git a/test/Ocelot.AcceptanceTests/.gitignore b/test/Ocelot.AcceptanceTests/.gitignore new file mode 100644 index 00000000..0ca27f04 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/.gitignore @@ -0,0 +1,234 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/test/Ocelot.AcceptanceTests/RouterTests.cs b/test/Ocelot.AcceptanceTests/RouterTests.cs new file mode 100644 index 00000000..43dc2e4d --- /dev/null +++ b/test/Ocelot.AcceptanceTests/RouterTests.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Ocelot; +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + public class RouterTests + { + public RouterTests() + { + } + + [Fact] + public void should_route_request() + { + + } + } +} diff --git a/test/Ocelot.AcceptanceTests/project.json b/test/Ocelot.AcceptanceTests/project.json new file mode 100644 index 00000000..da9f68a7 --- /dev/null +++ b/test/Ocelot.AcceptanceTests/project.json @@ -0,0 +1,37 @@ +{ + "version": "1.0.0-*", + + "testRunner": "xunit", + + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.0", + "type": "platform" + }, + "Microsoft.AspNetCore.Mvc": "1.0.0", + "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", + "Microsoft.Extensions.Configuration.Json": "1.0.0", + "Microsoft.Extensions.Logging": "1.0.0", + "Microsoft.Extensions.Logging.Console": "1.0.0", + "Microsoft.Extensions.Logging.Debug": "1.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", + "Microsoft.AspNetCore.Http": "1.0.0", + "Ocelot.Library": "1.0.0-*", + "Ocelot": "1.0.0-*", + "xunit": "2.1.0", + "dotnet-test-xunit": "2.2.0-preview2-build1029", + "Shouldly": "2.8.0" + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": [ + "dotnet5.6", + "portable-net45+win8" + ] + } + } +} diff --git a/test/Ocelot.UnitTests/RouterTests.cs b/test/Ocelot.UnitTests/UpstreamRouterTests.cs similarity index 90% rename from test/Ocelot.UnitTests/RouterTests.cs rename to test/Ocelot.UnitTests/UpstreamRouterTests.cs index e9f32823..e2060f6e 100644 --- a/test/Ocelot.UnitTests/RouterTests.cs +++ b/test/Ocelot.UnitTests/UpstreamRouterTests.cs @@ -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 _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) diff --git a/test/Ocelot.UnitTests/UrlMapperTests.cs b/test/Ocelot.UnitTests/UrlMapperTests.cs new file mode 100644 index 00000000..b35d9933 --- /dev/null +++ b/test/Ocelot.UnitTests/UrlMapperTests.cs @@ -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 == '{'; + } + } +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/UrlPathRouterTests.cs b/test/Ocelot.UnitTests/UrlPathRouterTests.cs new file mode 100644 index 00000000..73cdc06e --- /dev/null +++ b/test/Ocelot.UnitTests/UrlPathRouterTests.cs @@ -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 _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(); + _response.Errors[0].Message.ShouldBe("This key has already been used"); + } + + private void ThenTheKeyDoesNotExist() + { + _getResponse.ShouldNotBeNull(); + _getResponse.ShouldBeOfType>(); + _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(); + } + } +} \ No newline at end of file