From af279f6d2b6cdae9a8dde676a8494ef730aa47a6 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Thu, 24 Jul 2014 09:08:13 -0700 Subject: [PATCH] #65 Add a QueryBuilder. --- HttpAbstractions.sln | 28 ++++++- .../Microsoft.AspNet.WebUtilities.kproj | 35 +++++++++ .../QueryBuilder.cs | 74 ++++++++++++++++++ .../project.json | 14 ++++ .../Microsoft.AspNet.WebUtilities.Tests.kproj | 35 +++++++++ .../QueryBuilderTests.cs | 77 +++++++++++++++++++ .../project.json | 17 ++++ 7 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.AspNet.WebUtilities/Microsoft.AspNet.WebUtilities.kproj create mode 100644 src/Microsoft.AspNet.WebUtilities/QueryBuilder.cs create mode 100644 src/Microsoft.AspNet.WebUtilities/project.json create mode 100644 test/Microsoft.AspNet.WebUtilities.Tests/Microsoft.AspNet.WebUtilities.Tests.kproj create mode 100644 test/Microsoft.AspNet.WebUtilities.Tests/QueryBuilderTests.cs create mode 100644 test/Microsoft.AspNet.WebUtilities.Tests/project.json diff --git a/HttpAbstractions.sln b/HttpAbstractions.sln index 46b9267189..cdea46535d 100644 --- a/HttpAbstractions.sln +++ b/HttpAbstractions.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.21806.0 +VisualStudioVersion = 14.0.21916.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5A15F1C-885A-452A-A731-B0173DDBD913}" EndProject @@ -29,6 +29,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Http.Exten EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Http.Extensions.Tests", "test\Microsoft.AspNet.Http.Extensions.Tests\Microsoft.AspNet.Http.Extensions.Tests.kproj", "{AE25EF21-7F91-4B86-B73E-AF746821D339}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.WebUtilities", "src\Microsoft.AspNet.WebUtilities\Microsoft.AspNet.WebUtilities.kproj", "{A2FB7838-0031-4FAD-BA3E-83C30B3AF406}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.WebUtilities.Tests", "test\Microsoft.AspNet.WebUtilities.Tests\Microsoft.AspNet.WebUtilities.Tests.kproj", "{93C10E50-BCBB-4D8E-9492-D46E1396225B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -149,6 +153,26 @@ Global {AE25EF21-7F91-4B86-B73E-AF746821D339}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {AE25EF21-7F91-4B86-B73E-AF746821D339}.Release|Mixed Platforms.Build.0 = Release|Any CPU {AE25EF21-7F91-4B86-B73E-AF746821D339}.Release|x86.ActiveCfg = Release|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Release|Any CPU.Build.0 = Release|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406}.Release|x86.ActiveCfg = Release|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Debug|x86.ActiveCfg = Debug|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Release|Any CPU.Build.0 = Release|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {93C10E50-BCBB-4D8E-9492-D46E1396225B}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -165,5 +189,7 @@ Global {16219571-3268-4D12-8689-12B7163DBA13} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} {CCC4363E-81E2-4058-94DD-00494E9E992A} = {A5A15F1C-885A-452A-A731-B0173DDBD913} {AE25EF21-7F91-4B86-B73E-AF746821D339} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} + {A2FB7838-0031-4FAD-BA3E-83C30B3AF406} = {A5A15F1C-885A-452A-A731-B0173DDBD913} + {93C10E50-BCBB-4D8E-9492-D46E1396225B} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.WebUtilities/Microsoft.AspNet.WebUtilities.kproj b/src/Microsoft.AspNet.WebUtilities/Microsoft.AspNet.WebUtilities.kproj new file mode 100644 index 0000000000..0e877575d0 --- /dev/null +++ b/src/Microsoft.AspNet.WebUtilities/Microsoft.AspNet.WebUtilities.kproj @@ -0,0 +1,35 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + Debug + AnyCPU + + + + a2fb7838-0031-4fad-ba3e-83c30b3af406 + Library + Microsoft.AspNet.WebUtilities + + + ConsoleDebugger + + + WebDebugger + + + + + 2.0 + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.WebUtilities/QueryBuilder.cs b/src/Microsoft.AspNet.WebUtilities/QueryBuilder.cs new file mode 100644 index 0000000000..a719db536d --- /dev/null +++ b/src/Microsoft.AspNet.WebUtilities/QueryBuilder.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.WebUtilities +{ + // The IEnumerable interface is required for the collection initialization syntax: new QueryBuilder() { { "key", "value" } }; + public class QueryBuilder : IEnumerable> + { + private IList> _params; + + public QueryBuilder() + { + _params = new List>(); + } + + public QueryBuilder(IEnumerable> parameters) + { + _params = new List>(parameters); + } + + public void Add(string key, string value) + { + _params.Add(new KeyValuePair(key, value)); + } + + public override string ToString() + { + var builder = new StringBuilder(); + bool first = true; + for (int i = 0; i < _params.Count; i++) + { + var pair = _params[i]; + builder.Append(first ? "?" : "&"); + first = false; + builder.Append(Uri.EscapeDataString(pair.Key)); + builder.Append("="); + builder.Append(Uri.EscapeDataString(pair.Value)); + } + + return builder.ToString(); + } + + public QueryString ToQueryString() + { + return new QueryString(ToString()); + } + + public override int GetHashCode() + { + return ToQueryString().GetHashCode(); + } + + public override bool Equals(object obj) + { + return ToQueryString().Equals(obj); + } + + public IEnumerator> GetEnumerator() + { + return _params.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _params.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.WebUtilities/project.json b/src/Microsoft.AspNet.WebUtilities/project.json new file mode 100644 index 0000000000..2c127c9880 --- /dev/null +++ b/src/Microsoft.AspNet.WebUtilities/project.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0-*", + "dependencies": { + "Microsoft.AspNet.Http": "1.0.0-*" + }, + "frameworks": { + "net45": {}, + "k10": { + "dependencies": { + "System.Runtime": "4.0.20.0" + } + } + } +} diff --git a/test/Microsoft.AspNet.WebUtilities.Tests/Microsoft.AspNet.WebUtilities.Tests.kproj b/test/Microsoft.AspNet.WebUtilities.Tests/Microsoft.AspNet.WebUtilities.Tests.kproj new file mode 100644 index 0000000000..a909df627d --- /dev/null +++ b/test/Microsoft.AspNet.WebUtilities.Tests/Microsoft.AspNet.WebUtilities.Tests.kproj @@ -0,0 +1,35 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + Debug + AnyCPU + + + + 93c10e50-bcbb-4d8e-9492-d46e1396225b + Library + Microsoft.AspNet.WebUtilities.Tests + + + ConsoleDebugger + + + WebDebugger + + + + + 2.0 + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebUtilities.Tests/QueryBuilderTests.cs b/test/Microsoft.AspNet.WebUtilities.Tests/QueryBuilderTests.cs new file mode 100644 index 0000000000..2de60d8a46 --- /dev/null +++ b/test/Microsoft.AspNet.WebUtilities.Tests/QueryBuilderTests.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.AspNet.WebUtilities +{ + public class QueryBuilderTests + { + [Fact] + public void EmptyQuery_NoQuestionMark() + { + var builder = new QueryBuilder(); + Assert.Equal(string.Empty, builder.ToString()); + } + + [Fact] + public void AddSimple_NoEncoding() + { + var builder = new QueryBuilder(); + builder.Add("key", "value"); + Assert.Equal("?key=value", builder.ToString()); + } + + [Fact] + public void AddSpace_PercentEncoded() + { + var builder = new QueryBuilder(); + builder.Add("key", "value 1"); + Assert.Equal("?key=value%201", builder.ToString()); + } + + [Fact] + public void AddReservedCharacters_PercentEncoded() + { + var builder = new QueryBuilder(); + builder.Add("key&", "value#"); + Assert.Equal("?key%26=value%23", builder.ToString()); + } + + [Fact] + public void AddMultipleValues_AddedInOrder() + { + var builder = new QueryBuilder(); + builder.Add("key1", "value1"); + builder.Add("key2", "value2"); + builder.Add("key3", "value3"); + Assert.Equal("?key1=value1&key2=value2&key3=value3", builder.ToString()); + } + + [Fact] + public void AddMultipleValuesViaConstructor_AddedInOrder() + { + var builder = new QueryBuilder(new[] + { + new KeyValuePair("key1", "value1"), + new KeyValuePair("key2", "value2"), + new KeyValuePair("key3", "value3"), + }); + Assert.Equal("?key1=value1&key2=value2&key3=value3", builder.ToString()); + } + + [Fact] + public void AddMultipleValuesViaInitializer_AddedInOrder() + { + var builder = new QueryBuilder() + { + { "key1", "value1" }, + { "key2", "value2" }, + { "key3", "value3" }, + }; + Assert.Equal("?key1=value1&key2=value2&key3=value3", builder.ToString()); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebUtilities.Tests/project.json b/test/Microsoft.AspNet.WebUtilities.Tests/project.json new file mode 100644 index 0000000000..b7217ebcea --- /dev/null +++ b/test/Microsoft.AspNet.WebUtilities.Tests/project.json @@ -0,0 +1,17 @@ +{ + "dependencies": { + "Microsoft.AspNet.Http": "", + "Microsoft.AspNet.WebUtilities": "", + "Xunit.KRunner": "1.0.0-*" + }, + "commands": { + "test": "Xunit.KRunner" + }, + "frameworks": { + "net45": { + "dependencies": { + "System.Runtime": "" + } + } + } +}