From 49f95d40f18a0e357a13b325a5f8314b665d4d66 Mon Sep 17 00:00:00 2001 From: Haacked Date: Thu, 1 Jan 2015 19:12:22 -0800 Subject: [PATCH] Remove Type parameter for IHttpClient send method This keeps the IHttpClient interface simpler and ensures the deserialization responsibility lies outside of that class. It only needed the Type parameter for a special case that could be handled in a better way. --- .../HttpClientAdapterTests.cs | 11 +- Octokit.Tests/Clients/BlobClientTests.cs | 4 +- .../Clients/IssueCommentsClientTests.cs | 6 +- Octokit.Tests/Clients/IssuesClientTests.cs | 5 +- .../Clients/RepositoryCommentsClientTests.cs | 4 +- Octokit.Tests/Clients/TreesClientTests.cs | 4 +- Octokit.Tests/Http/ConnectionTests.cs | 167 +++++++++--------- Octokit.Tests/Http/HttpClientAdapterTests.cs | 20 ++- Octokit.Tests/Http/JsonHttpPipelineTests.cs | 12 +- Octokit/Helpers/HttpClientExtensions.cs | 8 +- Octokit/Http/ApiResponse.cs | 16 ++ Octokit/Http/Connection.cs | 16 +- Octokit/Http/HttpClientAdapter.cs | 34 ++-- Octokit/Http/IHttpClient.cs | 5 +- Octokit/Http/IResponse.cs | 16 ++ Octokit/Http/JsonHttpPipeline.cs | 7 +- 16 files changed, 183 insertions(+), 152 deletions(-) diff --git a/Octokit.Tests.Integration/HttpClientAdapterTests.cs b/Octokit.Tests.Integration/HttpClientAdapterTests.cs index b7bdce39..ead3b4f1 100644 --- a/Octokit.Tests.Integration/HttpClientAdapterTests.cs +++ b/Octokit.Tests.Integration/HttpClientAdapterTests.cs @@ -23,13 +23,14 @@ public class HttpClientAdapterTests Method = HttpMethod.Get }; - var imageBytes = await httpClient.Send(request, CancellationToken.None); + var response = await httpClient.Send(request, CancellationToken.None); // Spot check some of dem bytes. - Assert.Equal(137, imageBytes.BodyAsObject[0]); - Assert.Equal(80, imageBytes.BodyAsObject[1]); - Assert.Equal(78, imageBytes.BodyAsObject[2]); - Assert.Equal(130, imageBytes.BodyAsObject.Last()); + var imageBytes = (byte[])response.BodyAsObject; + Assert.Equal(137, imageBytes[0]); + Assert.Equal(80, imageBytes[1]); + Assert.Equal(78, imageBytes[2]); + Assert.Equal(130, imageBytes.Last()); } } } diff --git a/Octokit.Tests/Clients/BlobClientTests.cs b/Octokit.Tests/Clients/BlobClientTests.cs index c53169bc..8d26eb3f 100644 --- a/Octokit.Tests/Clients/BlobClientTests.cs +++ b/Octokit.Tests/Clients/BlobClientTests.cs @@ -74,14 +74,14 @@ namespace Octokit.Tests.Clients "\"sha\": \"3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15\"," + "\"size\": 100" + "}"; - var response = new ApiResponse + var httpResponse = new Response { Body = blobResponseJson, ContentType = "application/json" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.NotNull(response.BodyAsObject); Assert.Equal(blobResponseJson, response.Body); diff --git a/Octokit.Tests/Clients/IssueCommentsClientTests.cs b/Octokit.Tests/Clients/IssueCommentsClientTests.cs index e3091e90..1fea9dc0 100644 --- a/Octokit.Tests/Clients/IssueCommentsClientTests.cs +++ b/Octokit.Tests/Clients/IssueCommentsClientTests.cs @@ -196,17 +196,17 @@ public class IssueCommentsClientTests "\"created_at\": \"2011-04-14T16:00:49Z\"," + "\"updated_at\": \"2011-04-14T16:00:49Z\"" + "}"; - var response = new ApiResponse + var httpResponse = new Response { Body = issueResponseJson, ContentType = "application/json" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.NotNull(response.BodyAsObject); - Assert.Equal(issueResponseJson, response.Body); + Assert.Equal(issueResponseJson, response.Body); Assert.Equal(1, response.BodyAsObject.Id); } } diff --git a/Octokit.Tests/Clients/IssuesClientTests.cs b/Octokit.Tests/Clients/IssuesClientTests.cs index b50c0f6c..3bced4b3 100644 --- a/Octokit.Tests/Clients/IssuesClientTests.cs +++ b/Octokit.Tests/Clients/IssuesClientTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using NSubstitute; using Octokit.Internal; -using Octokit.Tests; using Octokit.Tests.Helpers; using Xunit; @@ -220,14 +219,14 @@ namespace Octokit.Tests.Clients "ed_events\",\"type\":\"User\",\"site_admin\":false},\"labels\":[],\"state\":\"open\",\"assignee" + "\":null,\"milestone\":null,\"comments\":0,\"created_at\":\"2013-10-22T17:02:48Z\",\"updated_at\"" + ":\"2013-10-22T17:02:48Z\",\"closed_at\":null,\"body\":\"A new unassigned issue\",\"closed_by\":null}"; - var response = new ApiResponse + var httpResponse = new Response { Body = issueResponseJson, ContentType = "application/json" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.NotNull(response.BodyAsObject); Assert.Equal(issueResponseJson, response.Body); diff --git a/Octokit.Tests/Clients/RepositoryCommentsClientTests.cs b/Octokit.Tests/Clients/RepositoryCommentsClientTests.cs index a073da85..fa7dedbb 100644 --- a/Octokit.Tests/Clients/RepositoryCommentsClientTests.cs +++ b/Octokit.Tests/Clients/RepositoryCommentsClientTests.cs @@ -206,14 +206,14 @@ public class RepositoryCommentsClientTests "\"updated_at\": \"2011-04-14T16:00:49Z\"" + "}"; - var response = new ApiResponse + var httpResponse = new Response { Body = commitCommentResponseJson, ContentType = "application/json" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.NotNull(response.BodyAsObject); Assert.Equal(commitCommentResponseJson, response.Body); diff --git a/Octokit.Tests/Clients/TreesClientTests.cs b/Octokit.Tests/Clients/TreesClientTests.cs index 28bcc1b4..fe554cdd 100644 --- a/Octokit.Tests/Clients/TreesClientTests.cs +++ b/Octokit.Tests/Clients/TreesClientTests.cs @@ -119,14 +119,14 @@ namespace Octokit.Tests "}" + "]" + "}"; - var response = new ApiResponse + var httpResponse = new Response { Body = issueResponseJson, ContentType = "application/json" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.NotNull(response.BodyAsObject); Assert.Equal(issueResponseJson, response.Body); diff --git a/Octokit.Tests/Http/ConnectionTests.cs b/Octokit.Tests/Http/ConnectionTests.cs index e61fb075..93912d22 100644 --- a/Octokit.Tests/Http/ConnectionTests.cs +++ b/Octokit.Tests/Http/ConnectionTests.cs @@ -3,20 +3,18 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; using NSubstitute; using Octokit.Internal; using Octokit.Tests.Helpers; using Xunit; -using Xunit.Extensions; namespace Octokit.Tests.Http { public class ConnectionTests { - const string ExampleUrl = "http://example.com"; - static readonly Uri ExampleUri = new Uri(ExampleUrl); + const string exampleUrl = "http://example.com"; + static readonly Uri _exampleUri = new Uri(exampleUrl); public class TheGetMethod { @@ -24,18 +22,18 @@ namespace Octokit.Tests.Http public async Task SendsProperlyFormattedRequest() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.GetResponse(new Uri("endpoint", UriKind.Relative)); - httpClient.Received(1).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && req.ContentType == null && req.Body == null && req.Method == HttpMethod.Get && @@ -46,10 +44,10 @@ namespace Octokit.Tests.Http public async Task CanMakeMutipleRequestsWithSameConnection() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -58,8 +56,8 @@ namespace Octokit.Tests.Http await connection.GetResponse(new Uri("endpoint", UriKind.Relative)); await connection.GetResponse(new Uri("endpoint", UriKind.Relative)); - httpClient.Received(3).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(3).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && req.Method == HttpMethod.Get && req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken); } @@ -68,7 +66,7 @@ namespace Octokit.Tests.Http public async Task ParsesApiInfoOnResponse() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse + IResponse response = new Response { Headers = { @@ -76,9 +74,9 @@ namespace Octokit.Tests.Http } }; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -92,10 +90,10 @@ namespace Octokit.Tests.Http public async Task ThrowsAuthorizationExceptionExceptionForUnauthorizedResponse() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse { StatusCode = HttpStatusCode.Unauthorized}; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response { StatusCode = HttpStatusCode.Unauthorized}; + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -115,11 +113,11 @@ namespace Octokit.Tests.Http string otpHeaderValue) { var httpClient = Substitute.For(); - IResponse response = new ApiResponse { StatusCode = HttpStatusCode.Unauthorized }; + IResponse response = new Response { StatusCode = HttpStatusCode.Unauthorized }; response.Headers[headerKey] = otpHeaderValue; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -142,14 +140,14 @@ namespace Octokit.Tests.Http TwoFactorType expectedFactorType) { var httpClient = Substitute.For(); - IResponse response = new ApiResponse + IResponse response = new Response { StatusCode = HttpStatusCode.Unauthorized, }; response.Headers[headerKey] = otpHeaderValue; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -164,15 +162,15 @@ namespace Octokit.Tests.Http public async Task ThrowsApiValidationExceptionFor422Response() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse + IResponse response = new Response { StatusCode = (HttpStatusCode)422, Body = @"{""errors"":[{""code"":""custom"",""field"":""key"",""message"":""key is " + @"already in use"",""resource"":""PublicKey""}],""message"":""Validation Failed""}" }; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -188,15 +186,15 @@ namespace Octokit.Tests.Http public async Task ThrowsRateLimitExceededExceptionForForbidderResponse() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse + IResponse response = new Response { StatusCode = HttpStatusCode.Forbidden, Body = "{\"message\":\"API rate limit exceeded. " + "See http://developer.github.com/v3/#rate-limiting for details.\"}" }; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -212,15 +210,15 @@ namespace Octokit.Tests.Http public async Task ThrowsLoginAttemptsExceededExceptionForForbiddenResponse() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse + IResponse response = new Response { StatusCode = HttpStatusCode.Forbidden, Body = "{\"message\":\"Maximum number of login attempts exceeded\"," + "\"documentation_url\":\"http://developer.github.com/v3\"}" }; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -236,14 +234,14 @@ namespace Octokit.Tests.Http public async Task ThrowsNotFoundExceptionForFileNotFoundResponse() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse + IResponse response = new Response { StatusCode = HttpStatusCode.NotFound, Body = "GONE BYE BYE!" }; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -258,14 +256,14 @@ namespace Octokit.Tests.Http public async Task ThrowsForbiddenExceptionForUnknownForbiddenResponse() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse + IResponse response = new Response { StatusCode = HttpStatusCode.Forbidden, Body = "YOU SHALL NOT PASS!" }; - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -283,18 +281,18 @@ namespace Octokit.Tests.Http public async Task SendsProperlyFormattedRequestWithProperAcceptHeader() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.GetHtml(new Uri("endpoint", UriKind.Relative)); - httpClient.Received(1).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && req.ContentType == null && req.Body == null && req.Method == HttpMethod.Get && @@ -310,18 +308,18 @@ namespace Octokit.Tests.Http { string data = SimpleJson.SerializeObject(new object()); var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.Patch(new Uri("endpoint", UriKind.Relative), new object()); - httpClient.Received(1).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && (string)req.Body == data && req.Method == HttpVerb.Patch && req.ContentType == "application/x-www-form-urlencoded" && @@ -331,19 +329,18 @@ namespace Octokit.Tests.Http [Fact] public async Task RunsConfiguredAppWithAcceptsOverride() { - string data = SimpleJson.SerializeObject(new object()); var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.Patch(new Uri("endpoint", UriKind.Relative), new object(), "custom/accepts"); - httpClient.Received(1).Send(Arg.Is(req => req.Headers["Accept"] == "custom/accepts"), Args.CancellationToken); + httpClient.Received(1).Send(Arg.Is(req => req.Headers["Accept"] == "custom/accepts"), Args.CancellationToken); } } @@ -354,18 +351,18 @@ namespace Octokit.Tests.Http { string data = SimpleJson.SerializeObject(new object()); var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.Put(new Uri("endpoint", UriKind.Relative), new object()); - httpClient.Received(1).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && (string)req.Body == data && req.Method == HttpMethod.Put && req.ContentType == "application/x-www-form-urlencoded" && @@ -377,18 +374,18 @@ namespace Octokit.Tests.Http { string data = SimpleJson.SerializeObject(new object()); var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.Put(new Uri("endpoint", UriKind.Relative), new object(), "two-factor"); - httpClient.Received(1).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && (string)req.Body == data && req.Method == HttpMethod.Put && req.Headers["X-GitHub-OTP"] == "two-factor" && @@ -404,18 +401,18 @@ namespace Octokit.Tests.Http { string data = SimpleJson.SerializeObject(new object()); var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.Post(new Uri("endpoint", UriKind.Relative), new object(), null, null); - httpClient.Received(1).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && req.ContentType == "application/x-www-form-urlencoded" && (string)req.Body == data && req.Method == HttpMethod.Post && @@ -426,10 +423,10 @@ namespace Octokit.Tests.Http public async Task SendsProperlyFormattedPostRequestWithCorrectHeaders() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -441,8 +438,8 @@ namespace Octokit.Tests.Http null, "application/arbitrary"); - httpClient.Received().Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received().Send(Arg.Is(req => + req.BaseAddress == _exampleUri && req.Body == body && req.Headers["Accept"] == "application/vnd.github.v3+json; charset=utf-8" && req.ContentType == "application/arbitrary" && @@ -454,10 +451,10 @@ namespace Octokit.Tests.Http public async Task SetsAcceptsHeader() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); @@ -469,7 +466,7 @@ namespace Octokit.Tests.Http "application/json", null); - httpClient.Received().Send(Arg.Is(req => + httpClient.Received().Send(Arg.Is(req => req.Headers["Accept"] == "application/json" && req.ContentType == "application/x-www-form-urlencoded"), Args.CancellationToken); } @@ -481,18 +478,18 @@ namespace Octokit.Tests.Http public async Task SendsProperlyFormattedDeleteRequest() { var httpClient = Substitute.For(); - IResponse response = new ApiResponse(); - httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + IResponse response = new Response(); + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); var connection = new Connection(new ProductHeaderValue("OctokitTests"), - ExampleUri, + _exampleUri, Substitute.For(), httpClient, Substitute.For()); await connection.Delete(new Uri("endpoint", UriKind.Relative)); - httpClient.Received(1).Send(Arg.Is(req => - req.BaseAddress == ExampleUri && + httpClient.Received(1).Send(Arg.Is(req => + req.BaseAddress == _exampleUri && req.Body == null && req.ContentType == null && req.Method == HttpMethod.Delete && diff --git a/Octokit.Tests/Http/HttpClientAdapterTests.cs b/Octokit.Tests/Http/HttpClientAdapterTests.cs index a514186f..5dec9fea 100644 --- a/Octokit.Tests/Http/HttpClientAdapterTests.cs +++ b/Octokit.Tests/Http/HttpClientAdapterTests.cs @@ -4,11 +4,11 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Octokit.Internal; using Xunit; -using Xunit.Extensions; namespace Octokit.Tests.Http { @@ -120,7 +120,7 @@ namespace Octokit.Tests.Http }; var tester = new HttpClientAdapterTester(); - var response = await tester.BuildResponseTester(responseMessage); + var response = await tester.BuildResponseTester(responseMessage); var firstHeader = response.Headers.First(); Assert.Equal("peanut", firstHeader.Key); @@ -141,25 +141,27 @@ namespace Octokit.Tests.Http StatusCode = HttpStatusCode.OK, Content = new ByteArrayContent(new byte[] { 0, 1, 1, 0, 1}), }; + responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png"); var tester = new HttpClientAdapterTester(); - var response = await tester.BuildResponseTester(responseMessage); + var response = await tester.BuildResponseTester(responseMessage); Assert.Equal(new byte[] { 0, 1, 1, 0, 1 }, response.BodyAsObject); Assert.Null(response.Body); - Assert.Null(response.ContentType); + Assert.Equal("image/png", response.ContentType); } - public async Task SetsContentType(HttpStatusCode httpStatusCode) + [Fact] + public async Task SetsContentType() { var responseMessage = new HttpResponseMessage { - StatusCode = httpStatusCode, + StatusCode = HttpStatusCode.OK, Content = new StringContent("{}", Encoding.UTF8, "application/json"), }; var tester = new HttpClientAdapterTester(); - var response = await tester.BuildResponseTester(responseMessage); + var response = await tester.BuildResponseTester(responseMessage); Assert.Equal("application/json", response.ContentType); } @@ -172,9 +174,9 @@ namespace Octokit.Tests.Http return BuildRequestMessage(request); } - public async Task> BuildResponseTester(HttpResponseMessage responseMessage) + public async Task BuildResponseTester(HttpResponseMessage responseMessage) { - return await BuildResponse(responseMessage); + return await BuildResponse(responseMessage); } } } diff --git a/Octokit.Tests/Http/JsonHttpPipelineTests.cs b/Octokit.Tests/Http/JsonHttpPipelineTests.cs index e35a6672..e558935f 100644 --- a/Octokit.Tests/Http/JsonHttpPipelineTests.cs +++ b/Octokit.Tests/Http/JsonHttpPipelineTests.cs @@ -107,14 +107,14 @@ namespace Octokit.Tests.Http public void DeserializesResponse() { const string data = "works"; - var response = new ApiResponse + var httpResponse = new Response { Body = SimpleJson.SerializeObject(data), ContentType = "application/json" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.NotNull(response.BodyAsObject); Assert.Equal(data, response.BodyAsObject); @@ -124,14 +124,14 @@ namespace Octokit.Tests.Http public void IgnoresResponsesNotIdentifiedAsJson() { const string data = "works"; - var response = new ApiResponse + var httpResponse = new ApiResponse { Body = SimpleJson.SerializeObject(data), ContentType = "text/html" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.Null(response.BodyAsObject); } @@ -176,14 +176,14 @@ namespace Octokit.Tests.Http ""url"": ""object-url"" }}"; - var response = new ApiResponse + var httpResponse = new Response { Body = data, ContentType = "application/json" }; var jsonPipeline = new JsonHttpPipeline(); - jsonPipeline.DeserializeResponse(response); + var response = jsonPipeline.DeserializeResponse(httpResponse); Assert.NotNull(response.BodyAsObject); Assert.Equal("tag-name", response.BodyAsObject.Tag); diff --git a/Octokit/Helpers/HttpClientExtensions.cs b/Octokit/Helpers/HttpClientExtensions.cs index 158ea2a1..bbf6aafb 100644 --- a/Octokit/Helpers/HttpClientExtensions.cs +++ b/Octokit/Helpers/HttpClientExtensions.cs @@ -7,16 +7,12 @@ namespace Octokit { public static class HttpClientExtensions { - /// - /// OBSOLETE - /// - [Obsolete("This will be removed in a future release")] - public static Task> Send(this IHttpClient httpClient, IRequest request) + public static Task Send(this IHttpClient httpClient, IRequest request) { Ensure.ArgumentNotNull(httpClient, "httpClient"); Ensure.ArgumentNotNull(request, "request"); - return httpClient.Send(request, CancellationToken.None); + return httpClient.Send(request, CancellationToken.None); } } } \ No newline at end of file diff --git a/Octokit/Http/ApiResponse.cs b/Octokit/Http/ApiResponse.cs index 8a2d19fb..0bbdf4c3 100644 --- a/Octokit/Http/ApiResponse.cs +++ b/Octokit/Http/ApiResponse.cs @@ -11,6 +11,22 @@ namespace Octokit.Internal Headers = new Dictionary(); } + public ApiResponse(IResponse response) + { + Ensure.ArgumentNotNull(response, "response"); + Headers = response.Headers; + Body = response.Body; + ResponseUri = response.ResponseUri; + ApiInfo = response.ApiInfo; + StatusCode = response.StatusCode; + ContentType = response.ContentType; + } + + public ApiResponse(IResponse response, T bodyAsObject) : this(response) + { + BodyAsObject = bodyAsObject; + } + object IResponse.BodyAsObject { get { return BodyAsObject; } diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index a3689bee..ea994023 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -412,26 +412,26 @@ namespace Octokit } } - Task> GetHtml(IRequest request) + async Task> GetHtml(IRequest request) { request.Headers.Add("Accept", "application/vnd.github.html"); - return RunRequest(request, CancellationToken.None); + var response = await RunRequest(request, CancellationToken.None); + return new ApiResponse(response, response.Body); } - async Task> Run(IRequest request, CancellationToken cancellationToken) + async Task> Run(IRequest request, CancellationToken cancellationToken) { _jsonPipeline.SerializeRequest(request); - var response = await RunRequest(request, cancellationToken).ConfigureAwait(false); - _jsonPipeline.DeserializeResponse(response); - return response; + var response = await RunRequest(request, cancellationToken).ConfigureAwait(false); + return _jsonPipeline.DeserializeResponse(response); } // THIS IS THE METHOD THAT EVERY REQUEST MUST GO THROUGH! - async Task> RunRequest(IRequest request, CancellationToken cancellationToken) + async Task RunRequest(IRequest request, CancellationToken cancellationToken) { request.Headers.Add("User-Agent", UserAgent); await _authenticator.Apply(request).ConfigureAwait(false); - var response = await _httpClient.Send(request, cancellationToken).ConfigureAwait(false); + var response = await _httpClient.Send(request, cancellationToken).ConfigureAwait(false); ApiInfoParser.ParseApiHttpHeaders(response); HandleErrors(response); return response; diff --git a/Octokit/Http/HttpClientAdapter.cs b/Octokit/Http/HttpClientAdapter.cs index 1e42c4f8..65aa3df9 100644 --- a/Octokit/Http/HttpClientAdapter.cs +++ b/Octokit/Http/HttpClientAdapter.cs @@ -17,16 +17,22 @@ namespace Octokit.Internal /// public class HttpClientAdapter : IHttpClient { - readonly IWebProxy webProxy; + readonly IWebProxy _webProxy; public HttpClientAdapter() { } public HttpClientAdapter(IWebProxy webProxy) { - this.webProxy = webProxy; + _webProxy = webProxy; } - public async Task> Send(IRequest request, CancellationToken cancellationToken) + /// + /// Sends the specified request and returns a response. + /// + /// A that represents the HTTP request + /// Used to cancel the request + /// A of + public async Task Send(IRequest request, CancellationToken cancellationToken) { Ensure.ArgumentNotNull(request, "request"); @@ -38,10 +44,10 @@ namespace Octokit.Internal { httpOptions.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; } - if (httpOptions.SupportsProxy && webProxy != null) + if (httpOptions.SupportsProxy && _webProxy != null) { httpOptions.UseProxy = true; - httpOptions.Proxy = webProxy; + httpOptions.Proxy = _webProxy; } var http = new HttpClient(httpOptions) @@ -54,11 +60,11 @@ namespace Octokit.Internal // Make the request var responseMessage = await http.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, cancellationToken) .ConfigureAwait(false); - return await BuildResponse(responseMessage).ConfigureAwait(false); + return await BuildResponse(responseMessage).ConfigureAwait(false); } } - protected async virtual Task> BuildResponse(HttpResponseMessage responseMessage) + protected async virtual Task BuildResponse(HttpResponseMessage responseMessage) { Ensure.ArgumentNotNull(responseMessage, "responseMessage"); @@ -69,10 +75,10 @@ namespace Octokit.Internal { if (content != null) { - var mediaType = responseMessage.Content.Headers.ContentType.MediaType; - + contentType = GetContentMediaTypeType(responseMessage.Content); + // We added support for downloading images. Let's constrain this appropriately. - if (!mediaType.StartsWith("image/")) + if (contentType == null || !contentType.StartsWith("image/")) { responseBody = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); } @@ -80,15 +86,13 @@ namespace Octokit.Internal { bodyAsObject = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false); } - - contentType = GetContentType(content); } } - var response = new ApiResponse + var response = new Response { Body = responseBody, - BodyAsObject = (T)bodyAsObject, + BodyAsObject = bodyAsObject, StatusCode = responseMessage.StatusCode, ContentType = contentType }; @@ -143,7 +147,7 @@ namespace Octokit.Internal return requestMessage; } - static string GetContentType(HttpContent httpContent) + static string GetContentMediaTypeType(HttpContent httpContent) { if (httpContent.Headers != null && httpContent.Headers.ContentType != null) { diff --git a/Octokit/Http/IHttpClient.cs b/Octokit/Http/IHttpClient.cs index a580d75b..0818c525 100644 --- a/Octokit/Http/IHttpClient.cs +++ b/Octokit/Http/IHttpClient.cs @@ -14,10 +14,9 @@ namespace Octokit.Internal /// /// Sends the specified request and returns a response. /// - /// The type of data to send /// A that represents the HTTP request /// Used to cancel the request - /// A of - Task> Send(IRequest request, CancellationToken cancellationToken); + /// A of + Task Send(IRequest request, CancellationToken cancellationToken); } } diff --git a/Octokit/Http/IResponse.cs b/Octokit/Http/IResponse.cs index 89b33fed..7c839a49 100644 --- a/Octokit/Http/IResponse.cs +++ b/Octokit/Http/IResponse.cs @@ -19,4 +19,20 @@ namespace Octokit HttpStatusCode StatusCode { get; set; } string ContentType { get; set; } } + + public class Response : IResponse + { + public Response() + { + Headers = new Dictionary(); + } + + public object BodyAsObject { get; set; } + public string Body { get; set; } + public Dictionary Headers { get; private set; } + public Uri ResponseUri { get; set; } + public ApiInfo ApiInfo { get; set; } + public HttpStatusCode StatusCode { get; set; } + public string ContentType { get; set; } + } } diff --git a/Octokit/Http/JsonHttpPipeline.cs b/Octokit/Http/JsonHttpPipeline.cs index eb3a4d0d..24cc4764 100644 --- a/Octokit/Http/JsonHttpPipeline.cs +++ b/Octokit/Http/JsonHttpPipeline.cs @@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using System.Net.Http; @@ -40,7 +39,7 @@ namespace Octokit.Internal request.Body = _serializer.Serialize(request.Body); } - public void DeserializeResponse(IResponse response) + public IResponse DeserializeResponse(IResponse response) { Ensure.ArgumentNotNull(response, "response"); @@ -60,9 +59,11 @@ namespace Octokit.Internal { body = "[" + body + "]"; } - response.BodyAsObject = _serializer.Deserialize(body); + var json = _serializer.Deserialize(body); + return new ApiResponse(response, json); } } + return new ApiResponse(response); } } }