From 6b7a6b7137b2ff80431ba6a5d6fde14936c0805d Mon Sep 17 00:00:00 2001 From: Haacked Date: Sat, 26 Oct 2013 10:26:46 -0700 Subject: [PATCH] Build user agent from user supplied product info Rather than create a default user agent, we'll build the user agent for the user of the library. We only require that the user provide us product information. fixes #61 --- .../AssigneesClientTests.cs | 3 +- Octokit.Tests.Integration/Helper.cs | 3 +- .../IssuesClientTests.cs | 3 +- .../MilestonesClientTests.cs | 3 +- .../MiscellaneousClientTests.cs | 7 +- .../ObservableRepositoriesClientTests.cs | 5 +- .../ReleasesClientTests.cs | 3 +- .../RepositoriesClientTests.cs | 35 +++---- Octokit.Tests.Integration/UsersClientTests.cs | 14 +-- Octokit.Tests/GitHubClientTests.cs | 22 +++-- Octokit.Tests/Http/ConnectionTests.cs | 74 ++++++-------- Octokit/GitHubClient.cs | 52 +++++++++- Octokit/Http/Connection.cs | 97 ++++++++++++++++--- clean-up-after-tests/Program.cs | 3 +- .../clean-up-after-tests.csproj | 1 + 15 files changed, 224 insertions(+), 101 deletions(-) diff --git a/Octokit.Tests.Integration/AssigneesClientTests.cs b/Octokit.Tests.Integration/AssigneesClientTests.cs index 768b2745..bfbb8b51 100644 --- a/Octokit.Tests.Integration/AssigneesClientTests.cs +++ b/Octokit.Tests.Integration/AssigneesClientTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Net.Http.Headers; using System.Threading.Tasks; using Octokit; using Octokit.Tests.Integration; @@ -12,7 +13,7 @@ public class AssigneesClientTests public AssigneesClientTests() { - _gitHubClient = new GitHubClient("Test Runner User Agent") + _gitHubClient = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests.Integration/Helper.cs b/Octokit.Tests.Integration/Helper.cs index 4eb5bf02..c89a386e 100644 --- a/Octokit.Tests.Integration/Helper.cs +++ b/Octokit.Tests.Integration/Helper.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Http.Headers; namespace Octokit.Tests.Integration { @@ -24,7 +25,7 @@ namespace Octokit.Tests.Integration public static void DeleteRepo(string owner, string name) { - var api = new GitHubClient("Integration Test Runner") { Credentials = Credentials }; + var api = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Credentials }; try { api.Repository.Delete(owner, name); diff --git a/Octokit.Tests.Integration/IssuesClientTests.cs b/Octokit.Tests.Integration/IssuesClientTests.cs index 273f2708..2405aae5 100644 --- a/Octokit.Tests.Integration/IssuesClientTests.cs +++ b/Octokit.Tests.Integration/IssuesClientTests.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Octokit; @@ -15,7 +16,7 @@ public class IssuesClientTests : IDisposable public IssuesClientTests() { - _gitHubClient = new GitHubClient("Test Runner User Agent") + _gitHubClient = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests.Integration/MilestonesClientTests.cs b/Octokit.Tests.Integration/MilestonesClientTests.cs index 2876ac9f..ab689ca1 100644 --- a/Octokit.Tests.Integration/MilestonesClientTests.cs +++ b/Octokit.Tests.Integration/MilestonesClientTests.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Net.Http.Headers; using System.Threading.Tasks; using Octokit; using Octokit.Tests.Integration; @@ -15,7 +16,7 @@ public class MilestonesClientTests : IDisposable public MilestonesClientTests() { - _gitHubClient = new GitHubClient("Test Runner User Agent") + _gitHubClient = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests.Integration/MiscellaneousClientTests.cs b/Octokit.Tests.Integration/MiscellaneousClientTests.cs index ae13ee58..c897245d 100644 --- a/Octokit.Tests.Integration/MiscellaneousClientTests.cs +++ b/Octokit.Tests.Integration/MiscellaneousClientTests.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Xunit; namespace Octokit.Tests.Integration @@ -10,7 +11,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task GetsAllTheEmojis() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -26,7 +27,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CanRenderGitHubFlavoredMarkdown() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests.Integration/Reactive/ObservableRepositoriesClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableRepositoriesClientTests.cs index ce71dd8a..d773aaf7 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableRepositoriesClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableRepositoriesClientTests.cs @@ -1,4 +1,5 @@ -using System.Reactive.Linq; +using System.Net.Http.Headers; +using System.Reactive.Linq; using System.Threading.Tasks; using Octokit.Reactive; using Xunit; @@ -12,7 +13,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsSpecifiedRepository() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests.Integration/ReleasesClientTests.cs b/Octokit.Tests.Integration/ReleasesClientTests.cs index 19c535e9..d0a0b38b 100644 --- a/Octokit.Tests.Integration/ReleasesClientTests.cs +++ b/Octokit.Tests.Integration/ReleasesClientTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Net.Http.Headers; using System.Threading.Tasks; using Xunit; @@ -11,7 +12,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsReleases() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests.Integration/RepositoriesClientTests.cs b/Octokit.Tests.Integration/RepositoriesClientTests.cs index 33889d92..0c6a653d 100644 --- a/Octokit.Tests.Integration/RepositoriesClientTests.cs +++ b/Octokit.Tests.Integration/RepositoriesClientTests.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Xunit; namespace Octokit.Tests.Integration @@ -10,7 +11,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesANewPublicRepository() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -42,7 +43,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesANewPrivateRepository() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -69,7 +70,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesARepositoryWithoutDownloads() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -96,7 +97,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesARepositoryWithoutIssues() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -123,7 +124,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesARepositoryWithoutAWiki() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -150,7 +151,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesARepositoryWithADescription() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -177,7 +178,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesARepositoryWithAHomepage() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -204,7 +205,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesARepositoryWithAutoInit() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -232,7 +233,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesARepositoryWithAGitignoreTemplate() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -264,7 +265,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task CreatesANewPublicRepository() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -303,7 +304,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task DeletesRepository() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -319,7 +320,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsSpecifiedRepository() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -334,7 +335,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsForkedRepository() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -351,7 +352,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsAllRepositoriesForOrganization() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -367,7 +368,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsReadmeForSeeGit() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -383,7 +384,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsReadmeHtmlForSeeGit() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests.Integration/UsersClientTests.cs b/Octokit.Tests.Integration/UsersClientTests.cs index 705ddd21..21f1f465 100644 --- a/Octokit.Tests.Integration/UsersClientTests.cs +++ b/Octokit.Tests.Integration/UsersClientTests.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Net.Http.Headers; using System.Reactive.Linq; using System.Threading.Tasks; using Octokit.Internal; @@ -14,7 +15,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsSpecifiedUser() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -28,7 +29,8 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsSpecifiedUserUsingAwaitableCredentialProvider() { - var github = new GitHubClient("Octokit Test Runner", new ObservableCredentialProvider()); + var github = new GitHubClient(new ProductHeaderValue("OctokitTests"), + new ObservableCredentialProvider()); // Get a user by username var user = await github.User.Get("tclem"); @@ -50,7 +52,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsSpecifiedUser() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; @@ -66,7 +68,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task FailsWhenNotAuthenticated() { - var github = new GitHubClient("Octokit Test Runner"); + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")); var userUpdate = new UserUpdate { Name = Helper.Credentials.Login, @@ -81,7 +83,7 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task FailsWhenAuthenticatedWithBadCredentials() { - var github = new GitHubClient("Octokit Test Runner") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = new Credentials(Helper.Credentials.Login, "bad-password") }; @@ -101,7 +103,7 @@ namespace Octokit.Tests.Integration { public async Task RetrievesEmailsForUser() { - var github = new GitHubClient("Test Runner User Agent") + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; diff --git a/Octokit.Tests/GitHubClientTests.cs b/Octokit.Tests/GitHubClientTests.cs index 89f52015..2f960bf7 100644 --- a/Octokit.Tests/GitHubClientTests.cs +++ b/Octokit.Tests/GitHubClientTests.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Http.Headers; using System.Threading.Tasks; using NSubstitute; using Octokit.Internal; @@ -13,7 +14,7 @@ namespace Octokit.Tests [Fact] public void CreatesAnonymousClientByDefault() { - var client = new GitHubClient("Test Runner User Agent"); + var client = new GitHubClient(new ProductHeaderValue("OctokitTests", "1.0")); Assert.Equal(AuthenticationType.Anonymous, client.Credentials.AuthenticationType); } @@ -21,7 +22,10 @@ namespace Octokit.Tests [Fact] public void CanCreateBasicAuthClient() { - var client = new GitHubClient("Test Runner") { Credentials = new Credentials("tclem", "pwd") }; + var client = new GitHubClient(new ProductHeaderValue("OctokitTests", "1.0")) + { + Credentials = new Credentials("tclem", "pwd") + }; Assert.Equal(AuthenticationType.Basic, client.Credentials.AuthenticationType); } @@ -29,7 +33,10 @@ namespace Octokit.Tests [Fact] public void CanCreateOauthClient() { - var client = new GitHubClient("Test Runner") { Credentials = new Credentials("token") }; + var client = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = new Credentials("token") + }; Assert.Equal(AuthenticationType.Oauth, client.Credentials.AuthenticationType); } @@ -40,7 +47,7 @@ namespace Octokit.Tests [Fact] public void IsSetToGitHubApiV3() { - var client = new GitHubClient("Test Runner"); + var client = new GitHubClient(new ProductHeaderValue("OctokitTests", "1.0")); Assert.Equal(new Uri("https://api.github.com"), client.BaseAddress); } @@ -51,7 +58,7 @@ namespace Octokit.Tests [Fact] public void DefaultsToAnonymous() { - var client = new GitHubClient("Test Runner"); + var client = new GitHubClient(new ProductHeaderValue("OctokitTests", "1.0")); Assert.Same(Credentials.Anonymous, client.Credentials); } @@ -59,7 +66,8 @@ namespace Octokit.Tests public void WhenSetCreatesInMemoryStoreThatReturnsSpecifiedCredentials() { var credentials = new Credentials("Peter", "Griffin"); - var client = new GitHubClient("Test Runner", Substitute.For()) + var client = new GitHubClient(new ProductHeaderValue("OctokitTests"), + Substitute.For()) { Credentials = credentials }; @@ -73,7 +81,7 @@ namespace Octokit.Tests { var credentialStore = Substitute.For(); credentialStore.GetCredentials().Returns(Task.Factory.StartNew(() => new Credentials("foo", "bar"))); - var client = new GitHubClient("Test Runner", credentialStore); + var client = new GitHubClient(new ProductHeaderValue("OctokitTests"), credentialStore); Assert.Equal("foo", client.Credentials.Login); Assert.Equal("bar", client.Credentials.Password); diff --git a/Octokit.Tests/Http/ConnectionTests.cs b/Octokit.Tests/Http/ConnectionTests.cs index 00f484d3..4da849cf 100644 --- a/Octokit.Tests/Http/ConnectionTests.cs +++ b/Octokit.Tests/Http/ConnectionTests.cs @@ -3,6 +3,7 @@ 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; @@ -25,7 +26,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -47,7 +48,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -76,7 +77,7 @@ namespace Octokit.Tests.Http }; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -93,7 +94,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse { StatusCode = HttpStatusCode.Unauthorized}; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -117,7 +118,7 @@ namespace Octokit.Tests.Http IResponse response = new ApiResponse { StatusCode = HttpStatusCode.Unauthorized }; response.Headers[headerKey] = otpHeaderValue; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -147,7 +148,7 @@ namespace Octokit.Tests.Http }; response.Headers[headerKey] = otpHeaderValue; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -170,7 +171,7 @@ namespace Octokit.Tests.Http @"already in use"",""resource"":""PublicKey""}],""message"":""Validation Failed""}" }; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -194,7 +195,7 @@ namespace Octokit.Tests.Http "See http://developer.github.com/v3/#rate-limiting for details.\"}" }; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -218,7 +219,7 @@ namespace Octokit.Tests.Http "\"documentation_url\":\"http://developer.github.com/v3\"}" }; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -241,7 +242,7 @@ namespace Octokit.Tests.Http Body = "GONE BYE BYE!" }; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -263,7 +264,7 @@ namespace Octokit.Tests.Http Body = "YOU SHALL NOT PASS!" }; httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -284,7 +285,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -311,7 +312,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -337,7 +338,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -360,7 +361,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -387,7 +388,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -409,7 +410,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -437,7 +438,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner User Agent", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -464,7 +465,7 @@ namespace Octokit.Tests.Http var httpClient = Substitute.For(); IResponse response = new ApiResponse(); httpClient.Send(Args.Request).Returns(Task.FromResult(response)); - var connection = new Connection("Test Runner", + var connection = new Connection(new ProductHeaderValue("OctokitTests"), ExampleUri, Substitute.For(), httpClient, @@ -486,8 +487,8 @@ namespace Octokit.Tests.Http [Fact] public void EnsuresAbsoluteBaseAddress() { - Assert.Throws(() => new Connection("Test Runner", new Uri("/foo", UriKind.Relative))); - Assert.Throws(() => new Connection("Test Runner", new Uri("/foo", UriKind.RelativeOrAbsolute))); + Assert.Throws(() => new Connection(new ProductHeaderValue("TestRunner"), new Uri("/foo", UriKind.Relative))); + Assert.Throws(() => new Connection(new ProductHeaderValue("TestRunner"), new Uri("/foo", UriKind.RelativeOrAbsolute))); } [Fact] @@ -495,55 +496,44 @@ namespace Octokit.Tests.Http { // 1 arg Assert.Throws(() => new Connection(null)); - Assert.Throws(() => new Connection("")); - // 2 args Assert.Throws(() => new Connection(null, new Uri("https://example.com"))); - Assert.Throws(() => new Connection("", new Uri("https://example.com"))); - Assert.Throws(() => new Connection("foo", (Uri)null)); + Assert.Throws(() => new Connection(new ProductHeaderValue("test"), (Uri)null)); // 3 args - Assert.Throws(() => new Connection("", - new Uri("https://example.com"), - Substitute.For())); Assert.Throws(() => new Connection(null, new Uri("https://example.com"), Substitute.For())); - Assert.Throws(() => new Connection("foo", + Assert.Throws(() => new Connection(new ProductHeaderValue("foo"), null, Substitute.For())); - Assert.Throws(() => new Connection("foo", + Assert.Throws(() => new Connection(new ProductHeaderValue("foo"), new Uri("https://example.com"), null)); // 5 Args - Assert.Throws(() => new Connection("" - , new Uri("https://example.com"), - Substitute.For(), - Substitute.For(), - Substitute.For())); Assert.Throws(() => new Connection(null , new Uri("https://example.com"), Substitute.For(), Substitute.For(), - Substitute.For())); - Assert.Throws(() => new Connection("foo", + Substitute.For())); + Assert.Throws(() => new Connection(new ProductHeaderValue("foo"), new Uri("https://example.com"), Substitute.For(), Substitute.For(), null)); - Assert.Throws(() => new Connection("foo", + Assert.Throws(() => new Connection(new ProductHeaderValue("foo"), new Uri("https://example.com"), Substitute.For(), null, Substitute.For())); - Assert.Throws(() => new Connection("foo", + Assert.Throws(() => new Connection(new ProductHeaderValue("foo"), new Uri("https://example.com"), null, Substitute.For(), Substitute.For())); - Assert.Throws(() => new Connection("foo", + Assert.Throws(() => new Connection(new ProductHeaderValue("foo"), null, Substitute.For(), Substitute.For(), @@ -553,10 +543,10 @@ namespace Octokit.Tests.Http [Fact] public void CreatesConnectionWithBaseAddress() { - var connection = new Connection("Test Runner User Agent", new Uri("https://github.com/")); + var connection = new Connection(new ProductHeaderValue("OctokitTests"), new Uri("https://github.com/")); Assert.Equal(new Uri("https://github.com/"), connection.BaseAddress); - Assert.Equal("Test Runner User Agent", connection.UserAgent); + Assert.True(connection.UserAgent.StartsWith("OctokitTests (")); } } } diff --git a/Octokit/GitHubClient.cs b/Octokit/GitHubClient.cs index f12deefc..97505210 100644 --- a/Octokit/GitHubClient.cs +++ b/Octokit/GitHubClient.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Http.Headers; using Octokit.Internal; namespace Octokit @@ -12,23 +13,64 @@ namespace Octokit /// Create a new instance of the GitHub API v3 client pointing to /// https://api.github.com/ /// - public GitHubClient(string userAgent) : this(new Connection(userAgent)) + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + public GitHubClient(ProductHeaderValue productInformation) + : this(new Connection(productInformation)) { } - public GitHubClient(string userAgent, ICredentialStore credentialStore) : this(new Connection(userAgent, credentialStore)) + /// + /// Create a new instance of the GitHub API v3 client pointing to + /// https://api.github.com/ + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + /// Provides credentials to the client when making requests. + public GitHubClient(ProductHeaderValue productInformation, ICredentialStore credentialStore) + : this(new Connection(productInformation, credentialStore)) { } - public GitHubClient(string userAgent, Uri baseAddress) : this(new Connection(userAgent, baseAddress)) + /// + /// Create a new instance of the GitHub API v3 client pointing to the specified baseAddress. + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + /// + /// The address to point this client to. Typically used for GitHub Enterprise + /// instances + public GitHubClient(ProductHeaderValue productInformation, Uri baseAddress) + : this(new Connection(productInformation, baseAddress)) { } - public GitHubClient(string userAgent, ICredentialStore credentialStore, Uri baseAddress) - : this(new Connection(userAgent, baseAddress, credentialStore)) + /// + /// Create a new instance of the GitHub API v3 client pointing to the specified baseAddress. + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + /// Provides credentials to the client when making requests. + /// + /// The address to point this client to. Typically used for GitHub Enterprise + /// instances + public GitHubClient(ProductHeaderValue productInformation, ICredentialStore credentialStore, Uri baseAddress) + : this(new Connection(productInformation, baseAddress, credentialStore)) { } + /// + /// Create a new instance of the GitHub API v3 client using the specified connection. + /// + /// The underlying used to make requests. public GitHubClient(IConnection connection) { Ensure.ArgumentNotNull(connection, "connection"); diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index c301f3d3..9ad5b4e7 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading.Tasks; using Octokit.Internal; @@ -20,40 +21,88 @@ namespace Octokit readonly IHttpClient _httpClient; readonly JsonHttpPipeline _jsonPipeline; - public Connection(string userAgent) : this(userAgent, _defaultGitHubApiUrl, _anonymousCredentials) + /// + /// Creates a new connection instance used to make requests of the GitHub API. + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + public Connection(ProductHeaderValue productInformation) + : this(productInformation, _defaultGitHubApiUrl, _anonymousCredentials) { } - public Connection(string userAgent, Uri baseAddress) : this(userAgent, baseAddress, _anonymousCredentials) + /// + /// Creates a new connection instance used to make requests of the GitHub API. + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + /// + /// The address to point this client to such as https://api.github.com or the URL to a GitHub Enterprise + /// instance. + public Connection(ProductHeaderValue productInformation, Uri baseAddress) + : this(productInformation, baseAddress, _anonymousCredentials) { } - public Connection(string userAgent, ICredentialStore credentialStore) : this(userAgent, _defaultGitHubApiUrl, credentialStore) + /// + /// Creates a new connection instance used to make requests of the GitHub API. + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + /// Provides credentials to the client when making requests. + public Connection(ProductHeaderValue productInformation, ICredentialStore credentialStore) + : this(productInformation, _defaultGitHubApiUrl, credentialStore) { } - public Connection(string userAgent, Uri baseAddress, ICredentialStore credentialStore) - : this(userAgent, baseAddress, credentialStore, new HttpClientAdapter(), new SimpleJsonSerializer()) + /// + /// Creates a new connection instance used to make requests of the GitHub API. + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + /// + /// The address to point this client to such as https://api.github.com or the URL to a GitHub Enterprise + /// instance. + /// Provides credentials to the client when making requests. + public Connection(ProductHeaderValue productInformation, Uri baseAddress, ICredentialStore credentialStore) + : this(productInformation, baseAddress, credentialStore, new HttpClientAdapter(), new SimpleJsonSerializer()) { } - public Connection(string userAgent, + /// + /// Creates a new connection instance used to make requests of the GitHub API. + /// + /// + /// The name (and optionally version) of the product using this library. This is sent to the server as part of + /// the user agent for analytics purposes. + /// + /// + /// The address to point this client to such as https://api.github.com or the URL to a GitHub Enterprise + /// instance. + /// Provides credentials to the client when making requests. + /// A raw used to make requests. + /// Class used to serialize and deserialize JSON requests. + public Connection( + ProductHeaderValue productInformation, Uri baseAddress, ICredentialStore credentialStore, IHttpClient httpClient, IJsonSerializer serializer) { - Ensure.ArgumentNotNullOrEmptyString(userAgent, "userAgent"); + Ensure.ArgumentNotNull(productInformation, "productInformation"); Ensure.ArgumentNotNull(baseAddress, "baseAddress"); Ensure.ArgumentNotNull(credentialStore, "credentialStore"); Ensure.ArgumentNotNull(httpClient, "httpClient"); Ensure.ArgumentNotNull(serializer, "serializer"); - if (String.IsNullOrWhiteSpace(userAgent)) - { - throw new ArgumentException("You must provide a User Agent"); - } - if (!baseAddress.IsAbsoluteUri) { throw new ArgumentException( @@ -61,7 +110,7 @@ namespace Octokit baseAddress), "baseAddress"); } - UserAgent = userAgent; + UserAgent = FormatUserAgent(productInformation); BaseAddress = baseAddress; _authenticator = new Authenticator(credentialStore); _httpClient = httpClient; @@ -297,5 +346,27 @@ namespace Octokit } return TwoFactorType.None; } + + static string FormatUserAgent(ProductHeaderValue productInformation) + { + return string.Format(CultureInfo.InvariantCulture, + "{0} ({1} {2}; {3}; {4}; Octokit {5})", + productInformation, +#if NETFX_CORE + // Microsoft doesn't want you changing your Windows Store Application based on the processor or + // Windows version. If we really wanted this information, we could do a best guess based on + // this approach: http://attackpattern.com/2013/03/device-information-in-windows-8-store-apps/ + // But I don't think we care all that much. + "WindowsRT", + "8+", + "unknown", +#else + Environment.OSVersion.Platform, + Environment.OSVersion.Version.ToString(3), + Environment.Is64BitOperatingSystem ? "amd64" : "x86", +#endif + CultureInfo.CurrentCulture.Name, + SolutionInfo.Version); + } } } diff --git a/clean-up-after-tests/Program.cs b/clean-up-after-tests/Program.cs index 7ae6f762..44d9c4a7 100644 --- a/clean-up-after-tests/Program.cs +++ b/clean-up-after-tests/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Http.Headers; using System.Text.RegularExpressions; using System.Threading.Tasks; using Octokit; @@ -31,7 +32,7 @@ namespace clean_up_after_tests static async Task DeleteRepos() { - var api = new GitHubClient("Octokit.net/clean-up-after-test.exe") + var api = new GitHubClient(new ProductHeaderValue("Octokit.net", "clean-up-after-test.exe")) { Credentials = Helper.Credentials }; diff --git a/clean-up-after-tests/clean-up-after-tests.csproj b/clean-up-after-tests/clean-up-after-tests.csproj index 662f0b53..392732b4 100644 --- a/clean-up-after-tests/clean-up-after-tests.csproj +++ b/clean-up-after-tests/clean-up-after-tests.csproj @@ -34,6 +34,7 @@ +