From 6a0e27274b3b849b78dcc40f49d98c8e85c76d48 Mon Sep 17 00:00:00 2001 From: Haacked Date: Wed, 31 Jul 2013 16:32:49 -0700 Subject: [PATCH] Allow passing query parameters to connection Change the API to support dictionary of query parameters. --- .../RepositoriesClientTests.cs | 14 ++-- .../Clients/AuthorizationsClientTests.cs | 4 +- .../Clients/AutoCompleteClientTests.cs | 4 +- .../Clients/OrganizationsClientTests.cs | 6 +- .../Clients/RepositoriesClientTests.cs | 20 +++--- Octokit.Tests/Http/ApiConnectionTests.cs | 14 ++-- Octokit/ApiExtensions.cs | 64 +++++++++++++++++++ Octokit/Clients/AutoCompleteClient.cs | 2 +- Octokit/Clients/RepositoriesClient.cs | 2 +- Octokit/Http/ApiConnection.cs | 30 ++++++--- Octokit/Http/Connection.cs | 21 ++++-- Octokit/Http/IApiConnection.cs | 8 +-- Octokit/Http/IConnection.cs | 7 +- Octokit/Http/ReadOnlyPagedCollection.cs | 2 +- Octokit/Octokit.csproj | 1 + Octokit/OctokitRT.csproj | 1 + 16 files changed, 145 insertions(+), 55 deletions(-) create mode 100644 Octokit/ApiExtensions.cs diff --git a/Octokit.Tests.Integration/RepositoriesClientTests.cs b/Octokit.Tests.Integration/RepositoriesClientTests.cs index 21110194..2d1e9de9 100644 --- a/Octokit.Tests.Integration/RepositoriesClientTests.cs +++ b/Octokit.Tests.Integration/RepositoriesClientTests.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; using FluentAssertions; -using Xunit; namespace Octokit.Tests.Integration { @@ -16,9 +16,9 @@ namespace Octokit.Tests.Integration Credentials = AutomationSettings.Current.GitHubCredentials }; - var repository = await github.Repository.Get("ReactiveCocoa", "ReactiveCocoa"); + Repository repository = await github.Repository.Get("haacked", "seegit"); - repository.CloneUrl.Should().Be("https://github.com/ReactiveCocoa/ReactiveCocoa.git"); + repository.CloneUrl.Should().Be("https://github.com/Haacked/SeeGit.git"); } } @@ -32,7 +32,7 @@ namespace Octokit.Tests.Integration Credentials = AutomationSettings.Current.GitHubCredentials }; - var repositories = await github.Repository.GetAllForOrg("github"); + IReadOnlyCollection repositories = await github.Repository.GetAllForOrg("github"); repositories.Count.Should().BeGreaterThan(80); } @@ -49,9 +49,9 @@ namespace Octokit.Tests.Integration }; // TODO: Change this to request github/Octokit.net once we make this OSS. - var readme = await github.Repository.GetReadme("haacked", "seegit"); + Readme readme = await github.Repository.GetReadme("haacked", "seegit"); readme.Name.Should().Be("README.md"); - var readMeHtml = await readme.GetHtmlContent(); + string readMeHtml = await readme.GetHtmlContent(); readMeHtml.Should().Contain(@"
WARNING: This is some haacky code."); } diff --git a/Octokit.Tests/Clients/AuthorizationsClientTests.cs b/Octokit.Tests/Clients/AuthorizationsClientTests.cs index ca9d90ef..02c5aaa1 100644 --- a/Octokit.Tests/Clients/AuthorizationsClientTests.cs +++ b/Octokit.Tests/Clients/AuthorizationsClientTests.cs @@ -31,7 +31,7 @@ namespace Octokit.Tests.Clients authEndpoint.GetAll(); - client.Received().GetAll(Arg.Is(u => u.ToString() == "/authorizations")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "/authorizations"), null); } } @@ -45,7 +45,7 @@ namespace Octokit.Tests.Clients authEndpoint.Get(1); - client.Received().Get(Arg.Is(u => u.ToString() == "/authorizations/1")); + client.Received().Get(Arg.Is(u => u.ToString() == "/authorizations/1"), null); } } diff --git a/Octokit.Tests/Clients/AutoCompleteClientTests.cs b/Octokit.Tests/Clients/AutoCompleteClientTests.cs index 4baaa94d..83f19bdd 100644 --- a/Octokit.Tests/Clients/AutoCompleteClientTests.cs +++ b/Octokit.Tests/Clients/AutoCompleteClientTests.cs @@ -37,14 +37,14 @@ namespace Octokit.Tests.Clients } }; var connection = Substitute.For(); - connection.GetAsync>(Args.Uri).Returns(Task.FromResult(response)); + connection.GetAsync>(Args.Uri, null).Returns(Task.FromResult(response)); var autoComplete = new AutoCompleteClient(connection); var emojis = await autoComplete.GetEmojis(); emojis.Count.Should().Be(2); connection.Received() - .GetAsync>(Arg.Is(u => u.ToString() == "/emojis")); + .GetAsync>(Arg.Is(u => u.ToString() == "/emojis"), null); } } } diff --git a/Octokit.Tests/Clients/OrganizationsClientTests.cs b/Octokit.Tests/Clients/OrganizationsClientTests.cs index cf7ff268..5e38adaf 100644 --- a/Octokit.Tests/Clients/OrganizationsClientTests.cs +++ b/Octokit.Tests/Clients/OrganizationsClientTests.cs @@ -33,7 +33,7 @@ namespace Octokit.Tests.Clients orgsClient.Get("orgName"); - client.Received().Get(Arg.Is(u => u.ToString() == "/orgs/orgName")); + client.Received().Get(Arg.Is(u => u.ToString() == "/orgs/orgName"), null); } [Fact] @@ -55,7 +55,7 @@ namespace Octokit.Tests.Clients orgs.GetAll("username"); - client.Received().GetAll(Arg.Is(u => u.ToString() == "/users/username/orgs")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "/users/username/orgs"), null); } [Fact] @@ -77,7 +77,7 @@ namespace Octokit.Tests.Clients orgs.GetAllForCurrent(); - client.Received().GetAll(Arg.Is(u => u.ToString() == "/user/orgs")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "/user/orgs"), null); } } } diff --git a/Octokit.Tests/Clients/RepositoriesClientTests.cs b/Octokit.Tests/Clients/RepositoriesClientTests.cs index 4abede76..73e088c6 100644 --- a/Octokit.Tests/Clients/RepositoriesClientTests.cs +++ b/Octokit.Tests/Clients/RepositoriesClientTests.cs @@ -35,7 +35,7 @@ namespace Octokit.Tests.Clients repositoriesClient.Get("fake", "repo"); - client.Received().Get(Arg.Is(u => u.ToString() == "/repos/fake/repo")); + client.Received().Get(Arg.Is(u => u.ToString() == "/repos/fake/repo"), null); } [Fact] @@ -59,7 +59,7 @@ namespace Octokit.Tests.Clients repositoriesClient.GetAllForCurrent(); client.Received() - .GetAll(Arg.Is(u => u.ToString() == "user/repos")); + .GetAll(Arg.Is(u => u.ToString() == "user/repos"), null); } } @@ -74,7 +74,7 @@ namespace Octokit.Tests.Clients repositoriesClient.GetAllForUser("username"); client.Received() - .GetAll(Arg.Is(u => u.ToString() == "/users/username/repos")); + .GetAll(Arg.Is(u => u.ToString() == "/users/username/repos"), null); } [Fact] @@ -97,7 +97,7 @@ namespace Octokit.Tests.Clients repositoriesClient.GetAllForOrg("orgname"); client.Received() - .GetAll(Arg.Is(u => u.ToString() == "/orgs/orgname/repos")); + .GetAll(Arg.Is(u => u.ToString() == "/orgs/orgname/repos"), null); } [Fact] @@ -124,18 +124,20 @@ namespace Octokit.Tests.Clients HtmlUrl = "https://github.example.com/readme" }; var client = Substitute.For>(); - client.GetItem(Args.Uri).Returns(Task.FromResult(readmeInfo)); - client.GetHtml(Args.Uri).Returns(Task.FromResult("README")); + client.GetItem(Args.Uri, null).Returns(Task.FromResult(readmeInfo)); + client.GetHtml(Args.Uri, null).Returns(Task.FromResult("README")); var reposEndpoint = new RepositoriesClient(client); var readme = await reposEndpoint.GetReadme("fake", "repo"); readme.Name.Should().Be("README.md"); - client.Received().GetItem(Arg.Is(u => u.ToString() == "/repos/fake/repo/readme")); - client.DidNotReceive().GetHtml(Arg.Is(u => u.ToString() == "https://github.example.com/readme")); + client.Received().GetItem(Arg.Is(u => u.ToString() == "/repos/fake/repo/readme"), + null); + client.DidNotReceive().GetHtml(Arg.Is(u => u.ToString() == "https://github.example.com/readme"), + null); var htmlReadme = await readme.GetHtmlContent(); htmlReadme.Should().Be("README"); - client.Received().GetHtml(Arg.Is(u => u.ToString() == "https://github.example.com/readme")); + client.Received().GetHtml(Arg.Is(u => u.ToString() == "https://github.example.com/readme"), null); } } } diff --git a/Octokit.Tests/Http/ApiConnectionTests.cs b/Octokit.Tests/Http/ApiConnectionTests.cs index 8bf90588..7a85dc65 100644 --- a/Octokit.Tests/Http/ApiConnectionTests.cs +++ b/Octokit.Tests/Http/ApiConnectionTests.cs @@ -19,7 +19,7 @@ namespace Octokit.Tests.Http var getUri = new Uri("/anything", UriKind.Relative); IResponse response = new ApiResponse { BodyAsObject = new object() }; var connection = Substitute.For(); - connection.GetAsync(Args.Uri).Returns(Task.FromResult(response)); + connection.GetAsync(Args.Uri, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.Get(getUri); @@ -44,10 +44,10 @@ namespace Octokit.Tests.Http var getUri = new Uri("/anything", UriKind.Relative); IResponse response = new ApiResponse { BodyAsObject = new object() }; var connection = Substitute.For(); - connection.GetAsync(Args.Uri).Returns(Task.FromResult(response)); + connection.GetAsync(Args.Uri, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); - var data = await apiConnection.GetItem(getUri); + var data = await apiConnection.GetItem(getUri, null); data.Should().BeSameAs(response.BodyAsObject); connection.Received().GetAsync(getUri); @@ -57,7 +57,7 @@ namespace Octokit.Tests.Http public async Task EnsuresArgumentNotNull() { var connection = new ApiConnection(Substitute.For()); - AssertEx.Throws(async () => await connection.GetItem(null)); + AssertEx.Throws(async () => await connection.GetItem(null, null)); } } @@ -69,7 +69,7 @@ namespace Octokit.Tests.Http var getUri = new Uri("/anything", UriKind.Relative); IResponse response = new ApiResponse { Body = "" }; var connection = Substitute.For(); - connection.GetHtml(Args.Uri).Returns(Task.FromResult(response)); + connection.GetHtml(Args.Uri, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.GetHtml(getUri); @@ -100,13 +100,13 @@ namespace Octokit.Tests.Http BodyAsObject = new List { new object(), new object() } }; var connection = Substitute.For(); - connection.GetAsync>(Args.Uri).Returns(Task.FromResult(response)); + connection.GetAsync>(Args.Uri, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.GetAll(getAllUri); data.Count.Should().Be(2); - connection.Received().GetAsync>(getAllUri); + connection.Received().GetAsync>(getAllUri, null); } [Fact] diff --git a/Octokit/ApiExtensions.cs b/Octokit/ApiExtensions.cs new file mode 100644 index 00000000..206914fa --- /dev/null +++ b/Octokit/ApiExtensions.cs @@ -0,0 +1,64 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using Octokit.Http; + +namespace Octokit +{ + public static class ApiExtensions + { + public static Task Get(this IApiConnection connection, Uri endpoint) + { + Ensure.ArgumentNotNull(connection, "connection"); + Ensure.ArgumentNotNull(endpoint, "endpoint"); + + return connection.Get(endpoint, null); + } + + public static Task> GetAll(this IApiConnection connection, Uri endpoint) + { + Ensure.ArgumentNotNull(connection, "connection"); + Ensure.ArgumentNotNull(endpoint, "endpoint"); + + return connection.GetAll(endpoint, null); + } + + public static Task GetHtml(this IApiConnection connection, Uri endpoint) + { + Ensure.ArgumentNotNull(connection, "connection"); + Ensure.ArgumentNotNull(endpoint, "endpoint"); + + return connection.GetHtml(endpoint, null); + } + + public static IDictionary ToDictionary(this object value) + { + Ensure.ArgumentNotNull(value, "value"); + + return value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public) + .Where(p => !p.IsSpecialName && p.CanRead) + .ToDictionary( + prop => prop.Name, + prop => Convert.ToString(prop.GetValue(value), CultureInfo.InvariantCulture)); + } + + public static Task> GetHtml(this IConnection connection, Uri endpoint) + { + Ensure.ArgumentNotNull(connection, "connection"); + Ensure.ArgumentNotNull(endpoint, "endpoint"); + + return connection.GetHtml(endpoint, null); + } + + public static async Task> GetAsync(this IConnection connection, Uri endpoint) + { + Ensure.ArgumentNotNull(connection, "connection"); + Ensure.ArgumentNotNull(endpoint, "endpoint"); + + return await connection.GetAsync(endpoint, null); + } + } +} diff --git a/Octokit/Clients/AutoCompleteClient.cs b/Octokit/Clients/AutoCompleteClient.cs index e01eefb5..01e13cd2 100644 --- a/Octokit/Clients/AutoCompleteClient.cs +++ b/Octokit/Clients/AutoCompleteClient.cs @@ -23,7 +23,7 @@ namespace Octokit.Clients public async Task> GetEmojis() { var endpoint = new Uri("/emojis", UriKind.Relative); - var response = await connection.GetAsync>(endpoint); + var response = await connection.GetAsync>(endpoint, null); return new ReadOnlyDictionary( response.BodyAsObject.ToDictionary(kvp => kvp.Key, kvp => new Uri(kvp.Value))); } diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index 934fd6ea..e09e0dbb 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -50,7 +50,7 @@ namespace Octokit.Clients Ensure.ArgumentNotNullOrEmptyString(name, "name"); var endpoint = "/repos/{0}/{1}/readme".FormatUri(owner, name); - var readmeInfo = await Client.GetItem(endpoint); + var readmeInfo = await Client.GetItem(endpoint, null); return new Readme(readmeInfo, Client); } } diff --git a/Octokit/Http/ApiConnection.cs b/Octokit/Http/ApiConnection.cs index 65b6a383..3869f24e 100644 --- a/Octokit/Http/ApiConnection.cs +++ b/Octokit/Http/ApiConnection.cs @@ -24,34 +24,34 @@ namespace Octokit.Http protected IConnection Connection { get; private set; } - public async Task Get(Uri endpoint) + public async Task Get(Uri endpoint, IDictionary parameters) { Ensure.ArgumentNotNull(endpoint, "endpoint"); - return await GetItem(endpoint); + return await GetItem(endpoint, parameters); } - public async Task GetItem(Uri endpoint) + public async Task GetItem(Uri endpoint, IDictionary parameters) { Ensure.ArgumentNotNull(endpoint, "endpoint"); - var response = await Connection.GetAsync(endpoint); + var response = await Connection.GetAsync(endpoint, parameters); return response.BodyAsObject; } - public async Task GetHtml(Uri endpoint) + public async Task GetHtml(Uri endpoint, IDictionary parameters) { Ensure.ArgumentNotNull(endpoint, "endpoint"); - var response = await Connection.GetHtml(endpoint); + var response = await Connection.GetHtml(endpoint, parameters); return response.Body; } - public async Task> GetAll(Uri endpoint) + public async Task> GetAll(Uri endpoint, IDictionary parameters) { Ensure.ArgumentNotNull(endpoint, "endpoint"); - return await pagination.GetAllPages(async () => await GetPage(endpoint)); + return await pagination.GetAllPages(async () => await GetPage(endpoint, parameters)); } public async Task Create(Uri endpoint, object data) @@ -74,6 +74,16 @@ namespace Octokit.Http return response.BodyAsObject; } + public async Task Put(Uri endpoint, object data) + { + Ensure.ArgumentNotNull(endpoint, "endpoint"); + Ensure.ArgumentNotNull(data, "data"); + + var response = await Connection.PostAsync(endpoint, data); + + return response.BodyAsObject; + } + public async Task Delete(Uri endpoint) { Ensure.ArgumentNotNull(endpoint, "endpoint"); @@ -81,11 +91,11 @@ namespace Octokit.Http await Connection.DeleteAsync(endpoint); } - async Task> GetPage(Uri endpoint) + async Task> GetPage(Uri endpoint, IDictionary parameters) { Ensure.ArgumentNotNull(endpoint, "endpoint"); - var response = await Connection.GetAsync>(endpoint); + var response = await Connection.GetAsync>(endpoint, parameters); return new ReadOnlyPagedCollection(response, Connection); } diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index e80256cc..b432557c 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Net.Http; using System.Threading.Tasks; @@ -57,7 +58,7 @@ namespace Octokit.Http apiInfoParser = new ApiInfoParser(); } - public async Task> GetAsync(Uri endpoint) + public async Task> GetAsync(Uri endpoint, IDictionary parameters) { Ensure.ArgumentNotNull(endpoint, "endpoint"); @@ -65,11 +66,11 @@ namespace Octokit.Http { Method = HttpMethod.Get, BaseAddress = BaseAddress, - Endpoint = endpoint + Endpoint = endpoint.ApplyParameters(parameters) }); } - public async Task> GetHtml(Uri endpoint) + public async Task> GetHtml(Uri endpoint, IDictionary parameters) { Ensure.ArgumentNotNull(endpoint, "endpoint"); @@ -77,7 +78,7 @@ namespace Octokit.Http { Method = HttpMethod.Get, BaseAddress = BaseAddress, - Endpoint = endpoint + Endpoint = endpoint.ApplyParameters(parameters) }); } @@ -96,13 +97,23 @@ namespace Octokit.Http } public async Task> PostAsync(Uri endpoint, object body) + { + return await SendData(endpoint, HttpMethod.Post, body); + } + + public async Task> PutAsync(Uri endpoint, object body) + { + return await SendData(endpoint, HttpMethod.Put, body); + } + + async Task> SendData(Uri endpoint, HttpMethod method, object body) { Ensure.ArgumentNotNull(endpoint, "endpoint"); Ensure.ArgumentNotNull(body, "body"); return await Run(new Request { - Method = HttpMethod.Post, + Method = method, BaseAddress = BaseAddress, Endpoint = endpoint, Body = body diff --git a/Octokit/Http/IApiConnection.cs b/Octokit/Http/IApiConnection.cs index 80e312ef..73e06011 100644 --- a/Octokit/Http/IApiConnection.cs +++ b/Octokit/Http/IApiConnection.cs @@ -13,10 +13,10 @@ namespace Octokit.Http { [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "It's fiiiine. It's fine. Trust us.")] - Task Get(Uri endpoint); - Task GetItem(Uri endpoint); - Task GetHtml(Uri endpoint); - Task> GetAll(Uri endpoint); + Task Get(Uri endpoint, IDictionary parameters); + Task GetItem(Uri endpoint, IDictionary parameters); + Task GetHtml(Uri endpoint, IDictionary parameters); + Task> GetAll(Uri endpoint, IDictionary parameters); Task Create(Uri endpoint, object data); Task Update(Uri endpoint, object data); Task Delete(Uri endpoint); diff --git a/Octokit/Http/IConnection.cs b/Octokit/Http/IConnection.cs index c82e8e7c..f89840fa 100644 --- a/Octokit/Http/IConnection.cs +++ b/Octokit/Http/IConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; @@ -6,11 +7,11 @@ namespace Octokit.Http { public interface IConnection { - Task> GetHtml(Uri endpoint); - - Task> GetAsync(Uri endpoint); + Task> GetHtml(Uri endpoint, IDictionary parameters); + Task> GetAsync(Uri endpoint, IDictionary parameters); Task> PatchAsync(Uri endpoint, object body); Task> PostAsync(Uri endpoint, object body); + Task> PutAsync(Uri endpoint, object body); [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] Task DeleteAsync(Uri endpoint); diff --git a/Octokit/Http/ReadOnlyPagedCollection.cs b/Octokit/Http/ReadOnlyPagedCollection.cs index efa8a152..37e23ff4 100644 --- a/Octokit/Http/ReadOnlyPagedCollection.cs +++ b/Octokit/Http/ReadOnlyPagedCollection.cs @@ -24,7 +24,7 @@ namespace Octokit.Http var nextPageUrl = info.GetNextPageUrl(); if (nextPageUrl == null) return null; - var response = await connection.GetAsync>(nextPageUrl); + var response = await connection.GetAsync>(nextPageUrl, null); return new ReadOnlyPagedCollection(response, connection); } } diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index a61a0f13..51236aa7 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -39,6 +39,7 @@ + diff --git a/Octokit/OctokitRT.csproj b/Octokit/OctokitRT.csproj index 4b098600..1ad90c04 100644 --- a/Octokit/OctokitRT.csproj +++ b/Octokit/OctokitRT.csproj @@ -102,6 +102,7 @@ +