diff --git a/Octokit.Reactive/Clients/ObservableBlobClient.cs b/Octokit.Reactive/Clients/ObservableBlobClient.cs index 7279f9bc..9cb91506 100644 --- a/Octokit.Reactive/Clients/ObservableBlobClient.cs +++ b/Octokit.Reactive/Clients/ObservableBlobClient.cs @@ -11,7 +11,7 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNull(client, "client"); - _client = client.Blob; + _client = client.GitDatabase.Blob; } /// diff --git a/Octokit.Reactive/Clients/ObservableSearchClient.cs b/Octokit.Reactive/Clients/ObservableSearchClient.cs index 4e6981fd..b7c47476 100644 --- a/Octokit.Reactive/Clients/ObservableSearchClient.cs +++ b/Octokit.Reactive/Clients/ObservableSearchClient.cs @@ -49,7 +49,7 @@ namespace Octokit.Reactive public IObservable SearchIssues(SearchIssuesRequest request) { Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.SearchIssues(), request.ToParametersDictionary()); + return _connection.GetAndFlattenAllPages(ApiUrls.SearchIssues(), request.Parameters); } /// diff --git a/Octokit.Tests.Integration/Clients/BranchesClientTests.cs b/Octokit.Tests.Integration/Clients/BranchesClientTests.cs new file mode 100644 index 00000000..c0ac66bc --- /dev/null +++ b/Octokit.Tests.Integration/Clients/BranchesClientTests.cs @@ -0,0 +1,39 @@ +using System; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Xunit; + +public class BranchesClientTests +{ + public class TheGetBranchesMethod : IDisposable + { + readonly Repository _repository; + readonly GitHubClient _github; + + public TheGetBranchesMethod() + { + _github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = Helper.Credentials + }; + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + _repository = _github.Repository.Create(new NewRepository { Name = repoName, AutoInit = true }).Result; + } + + [IntegrationTest] + public async Task ReturnsBranches() + { + var branches = await _github.Repository.GetAllBranches(_repository.Owner.Login, _repository.Name); + + Assert.NotEmpty(branches); + Assert.Equal(branches[0].Name, "master"); + } + + public void Dispose() + { + Helper.DeleteRepo(_repository); + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Clients/ReferencesClientTests.cs b/Octokit.Tests.Integration/Clients/ReferencesClientTests.cs index 46c6a175..c875bd9a 100644 --- a/Octokit.Tests.Integration/Clients/ReferencesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/ReferencesClientTests.cs @@ -62,11 +62,16 @@ public class ReferencesClientTests : IDisposable Assert.NotEmpty(list); } - [IntegrationTest(Skip = "See https://github.com/octokit/octokit.net/issues/242 and https://github.com/octokit/octokit.net/issues/238 for the relevant issues we need to address")] + [IntegrationTest] public async Task CanGetErrorForInvalidNamespace() { - await AssertEx.Throws( - async () => { await _fixture.GetAllForSubNamespace("octokit", "octokit.net", "666"); }); + var owner = "octokit"; + var repo = "octokit.net"; + var subNamespace = "666"; + + var result = await AssertEx.Throws( + async () => { await _fixture.GetAllForSubNamespace(owner, repo, subNamespace); }); + Assert.Equal(string.Format("{0} was not found.", ApiUrls.Reference(owner, repo, subNamespace)), result.Message); } [IntegrationTest] diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index fa8beaf1..cf1aa4aa 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -59,6 +59,7 @@ + diff --git a/Octokit.Tests/Clients/ReleasesClientTests.cs b/Octokit.Tests/Clients/ReleasesClientTests.cs index e2869081..8a840857 100644 --- a/Octokit.Tests/Clients/ReleasesClientTests.cs +++ b/Octokit.Tests/Clients/ReleasesClientTests.cs @@ -21,7 +21,7 @@ namespace Octokit.Tests.Clients client.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/releases"), null, - "application/vnd.github.manifold-preview"); + "application/vnd.github.v3"); } [Fact] @@ -47,7 +47,7 @@ namespace Octokit.Tests.Clients client.Received().Post(Arg.Is(u => u.ToString() == "repos/fake/repo/releases"), data, - "application/vnd.github.manifold-preview"); + "application/vnd.github.v3"); } [Fact] @@ -82,7 +82,7 @@ namespace Octokit.Tests.Clients client.Received().Post( Arg.Is(u => u.ToString() == "https://uploads.test.dev/does/not/matter/releases/1/assets?name=example.zip"), rawData, - "application/vnd.github.manifold-preview", + "application/vnd.github.v3", Arg.Is(contentType => contentType == "application/zip")); } diff --git a/Octokit.Tests/Clients/RepositoriesClientTests.cs b/Octokit.Tests/Clients/RepositoriesClientTests.cs index bb517f26..7c0b998e 100644 --- a/Octokit.Tests/Clients/RepositoriesClientTests.cs +++ b/Octokit.Tests/Clients/RepositoriesClientTests.cs @@ -249,5 +249,20 @@ namespace Octokit.Tests.Clients Assert.Equal("README", readme); } } + + public class TheGetAllBranchesMethod + { + [Fact] + public void ReturnsBranches() + { + var connection = Substitute.For(); + var client = new RepositoriesClient(connection); + + client.GetAllBranches("owner", "name"); + + connection.Received() + .GetAll(Arg.Is(u => u.ToString() == "repos/owner/name/branches")); + } + } } } diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index c6bb8298..8bf87c96 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -274,7 +274,7 @@ namespace Octokit.Tests.Clients //get repos where the Description contains rails and user/org is 'github' var request = new SearchRepositoriesRequest("rails"); request.Sort = RepoSearchSort.Forks; - + client.SearchRepo(request); connection.Received().GetAll(Arg.Is(u => u.ToString() == "search/repositories"), Arg.Any>()); @@ -298,6 +298,522 @@ namespace Octokit.Tests.Clients var client = new SearchClient(Substitute.For()); AssertEx.Throws(async () => await client.SearchIssues(null)); } + + [Fact] + public void TestingTheTermParameter() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("pub"); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "pub")); + } + + [Fact] + public void TestingTheSortParameter() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.SortField = IssueSearchSort.Comments; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => + d["sort"] == "comments")); + } + + [Fact] + public void TestingTheOrderParameter() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.SortField = IssueSearchSort.Updated; + request.Order = SortDirection.Ascending; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => + d["sort"] == "updated" && + d["order"] == "asc")); + } + + [Fact] + public void TestingTheDefaultOrderParameter() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => + d["order"] == "desc")); + } + + [Fact] + public void TestingTheInQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.In = new[] { IssueInQualifier.Comment }; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u=>u.ToString() == "search/issues"), + Arg.Is>(d=>d["q"] == "something+in:comment")); + } + + [Fact] + public void TestingTheInQualifiers_Multiple() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.In = new[] { IssueInQualifier.Body, IssueInQualifier.Title }; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+in:body,title")); + } + + [Fact] + public void TestingTheTypeQualifier_Issue() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Type = IssueTypeQualifier.Issue; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+type:issue")); + } + + [Fact] + public void TestingTheTypeQualifier_PR() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Type = IssueTypeQualifier.PR; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+type:pr")); + } + + [Fact] + public void TestingTheAuthorQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Author = "alfhenrik"; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+author:alfhenrik")); + } + + [Fact] + public void TestingTheAssigneeQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Assignee = "alfhenrik"; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+assignee:alfhenrik")); + } + + [Fact] + public void TestingTheMentionsQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Mentions = "alfhenrik"; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+mentions:alfhenrik")); + } + + [Fact] + public void TestingTheCommenterQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Commenter = "alfhenrik"; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+commenter:alfhenrik")); + } + + [Fact] + public void TestingTheInvolvesQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Involves = "alfhenrik"; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+involves:alfhenrik")); + } + + [Fact] + public void TestingTheStateQualifier_Open() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.State = ItemState.Open; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+state:Open")); + } + + [Fact] + public void TestingTheStateQualifier_Closed() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.State = ItemState.Closed; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+state:Closed")); + } + + [Fact] + public void TestingTheLabelsQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Labels = new[] { "bug" }; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+label:bug")); + } + + [Fact] + public void TestingTheLabelsQualifier_Multiple() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Labels = new[] { "bug", "feature" }; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+label:bug+label:feature")); + } + + [Fact] + public void TestingTheLanguageQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Language = Language.CSharp; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+language:CSharp")); + } + + [Fact] + public void TestingTheCreatedQualifier_GreaterThan() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Created = DateRange.GreaterThan(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+created:>2014-01-01")); + } + + [Fact] + public void TestingTheCreatedQualifier_GreaterThanOrEquals() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Created = DateRange.GreaterThanOrEquals(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+created:>=2014-01-01")); + } + + [Fact] + public void TestingTheCreatedQualifier_LessThan() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Created = DateRange.LessThan(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+created:<2014-01-01")); + } + + [Fact] + public void TestingTheCreatedQualifier_LessThanOrEquals() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Created = DateRange.LessThanOrEquals(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+created:<=2014-01-01")); + } + + [Fact] + public void TestingTheUpdatedQualifier_GreaterThan() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Updated = DateRange.GreaterThan(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+updated:>2014-01-01")); + } + + [Fact] + public void TestingTheUpdatedQualifier_GreaterThanOrEquals() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Updated = DateRange.GreaterThanOrEquals(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+updated:>=2014-01-01")); + } + + [Fact] + public void TestingTheUpdatedQualifier_LessThan() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Updated = DateRange.LessThan(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+updated:<2014-01-01")); + } + + [Fact] + public void TestingTheUpdatedQualifier_LessThanOrEquals() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Updated = DateRange.LessThanOrEquals(new DateTime(2014, 1, 1)); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+updated:<=2014-01-01")); + } + + [Fact] + public void TestingTheCommentsQualifier_GreaterThan() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Comments = Range.GreaterThan(10); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+comments:>10")); + } + + [Fact] + public void TestingTheCommentsQualifier_GreaterThanOrEqual() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Comments = Range.GreaterThanOrEquals(10); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+comments:>=10")); + } + + [Fact] + public void TestingTheCommentsQualifier_LessThan() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Comments = Range.LessThan(10); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+comments:<10")); + } + + [Fact] + public void TestingTheCommentsQualifier_LessThanOrEqual() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Comments = Range.LessThanOrEquals(10); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+comments:<=10")); + } + + [Fact] + public void TestingTheCommentsQualifier_Range() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Comments = new Range(10, 20); + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+comments:10..20")); + } + + [Fact] + public void TestingTheUserQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.User = "alfhenrik"; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+user:alfhenrik")); + } + + [Fact] + public void TestingTheRepoQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Repo = "octokit.net"; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == "something+repo:octokit.net")); + } + + [Fact] + public void TestingTheRepoAndUserAndLabelQualifier() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + var request = new SearchIssuesRequest("something"); + request.Repo = "octokit.net"; + request.User = "alfhenrik"; + request.Labels = new[] { "bug" }; + + client.SearchIssues(request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/issues"), + Arg.Is>(d => d["q"] == + "something+label:bug+user:alfhenrik+repo:octokit.net")); + } } public class TheSearchCodeMethod @@ -308,7 +824,9 @@ namespace Octokit.Tests.Clients var connection = Substitute.For(); var client = new SearchClient(connection); client.SearchCode(new SearchCodeRequest("something")); - connection.Received().GetAll(Arg.Is(u => u.ToString() == "search/code"), Arg.Any>()); + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "search/code"), + Arg.Any>()); } [Fact] diff --git a/Octokit.Tests/Http/JsonHttpPipelineTests.cs b/Octokit.Tests/Http/JsonHttpPipelineTests.cs index 63159447..1fe28547 100644 --- a/Octokit.Tests/Http/JsonHttpPipelineTests.cs +++ b/Octokit.Tests/Http/JsonHttpPipelineTests.cs @@ -34,13 +34,13 @@ namespace Octokit.Tests.Http public void DoesNotChangeExistingAcceptsHeader() { var request = new Request(); - request.Headers.Add("Accept", "application/vnd.github.manifold-preview; charset=utf-8"); + request.Headers.Add("Accept", "application/vnd.github.v3; charset=utf-8"); var jsonPipeline = new JsonHttpPipeline(); jsonPipeline.SerializeRequest(request); Assert.Contains("Accept", request.Headers.Keys); - Assert.Equal("application/vnd.github.manifold-preview; charset=utf-8", request.Headers["Accept"]); + Assert.Equal("application/vnd.github.v3; charset=utf-8", request.Headers["Accept"]); } [Fact] diff --git a/Octokit.Tests/Reactive/ObservableBlobClientTests.cs b/Octokit.Tests/Reactive/ObservableBlobClientTests.cs index 96907282..e914471c 100644 --- a/Octokit.Tests/Reactive/ObservableBlobClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableBlobClientTests.cs @@ -20,7 +20,7 @@ namespace Octokit.Tests.Reactive client.Get("fake", "repo", "123456ABCD"); - gitHubClient.Blob.Received().Get("fake", "repo", "123456ABCD"); + gitHubClient.GitDatabase.Blob.Received().Get("fake", "repo", "123456ABCD"); } [Fact] @@ -48,7 +48,7 @@ namespace Octokit.Tests.Reactive client.Create("fake", "repo", newBlob); - gitHubClient.Blob.Received().Create("fake", "repo", newBlob); + gitHubClient.GitDatabase.Blob.Received().Create("fake", "repo", newBlob); } [Fact] diff --git a/Octokit/Clients/ApiPagination.cs b/Octokit/Clients/ApiPagination.cs index a37f78e9..2eb51484 100644 --- a/Octokit/Clients/ApiPagination.cs +++ b/Octokit/Clients/ApiPagination.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; #endif using System.Threading.Tasks; +using Octokit.Internal; +using System.Net; namespace Octokit { @@ -15,17 +17,30 @@ namespace Octokit /// public class ApiPagination : IApiPagination { - public async Task> GetAllPages(Func>> getFirstPage) + public async Task> GetAllPages(Func>> getFirstPage, Uri uri) { Ensure.ArgumentNotNull(getFirstPage, "getFirstPage"); - - var page = await getFirstPage().ConfigureAwait(false); - var allItems = new List(page); - while ((page = await page.GetNextPage().ConfigureAwait(false)) != null) + try { - allItems.AddRange(page); + var page = await getFirstPage().ConfigureAwait(false); + + var allItems = new List(page); + while ((page = await page.GetNextPage().ConfigureAwait(false)) != null) + { + allItems.AddRange(page); + } + return new ReadOnlyCollection(allItems); + } + catch (NotFoundException) + { + throw new NotFoundException( + new ApiResponse() + { + StatusCode = HttpStatusCode.NotFound, + Body = string.Format("{0} was not found.", uri.OriginalString) + }); + } - return new ReadOnlyCollection(allItems); } } } diff --git a/Octokit/Clients/IRepositoriesClient.cs b/Octokit/Clients/IRepositoriesClient.cs index 8e98318b..883f9781 100644 --- a/Octokit/Clients/IRepositoriesClient.cs +++ b/Octokit/Clients/IRepositoriesClient.cs @@ -143,5 +143,17 @@ namespace Octokit /// See the Collaborators API documentation for more details /// IRepoCollaboratorsClient RepoCollaborators { get; } + + /// + /// Gets all the branches for the specified repository. + /// + /// + /// See the API documentation for more details + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// + Task> GetAllBranches(string owner, string name); } } diff --git a/Octokit/Clients/ReleasesClient.cs b/Octokit/Clients/ReleasesClient.cs index 7ae5994c..6ba6d73e 100644 --- a/Octokit/Clients/ReleasesClient.cs +++ b/Octokit/Clients/ReleasesClient.cs @@ -37,7 +37,7 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(name, "repository"); var endpoint = ApiUrls.Releases(owner, name); - return ApiConnection.GetAll(endpoint, null, "application/vnd.github.manifold-preview"); + return ApiConnection.GetAll(endpoint, null, "application/vnd.github.v3"); } /// @@ -58,7 +58,7 @@ namespace Octokit Ensure.ArgumentNotNull(data, "data"); var endpoint = ApiUrls.Releases(owner, name); - return ApiConnection.Post(endpoint, data, "application/vnd.github.manifold-preview"); + return ApiConnection.Post(endpoint, data, "application/vnd.github.v3"); } /// @@ -80,7 +80,7 @@ namespace Octokit return ApiConnection.Post( endpoint, data.RawData, - "application/vnd.github.manifold-preview", + "application/vnd.github.v3", data.ContentType); } } diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index c91e1f7d..7ec5e31d 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -1,4 +1,4 @@ -using System; +using System; #if NET_45 using System.Collections.Generic; #endif @@ -203,5 +203,22 @@ namespace Octokit /// See the Collaborators API documentation for more details /// public IRepoCollaboratorsClient RepoCollaborators { get; private set; } + + + /// + /// Gets all the branches for the specified repository. + /// + /// + /// See the API documentation for more details + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// + public Task> GetAllBranches(string owner, string name) + { + var endpoint = ApiUrls.RepoBranches(owner, name); + return ApiConnection.GetAll(endpoint); + } } } diff --git a/Octokit/Clients/SearchClient.cs b/Octokit/Clients/SearchClient.cs index b0d28d2a..abe8b905 100644 --- a/Octokit/Clients/SearchClient.cs +++ b/Octokit/Clients/SearchClient.cs @@ -52,7 +52,7 @@ namespace Octokit public Task> SearchIssues(SearchIssuesRequest search) { Ensure.ArgumentNotNull(search, "search"); - return ApiConnection.GetAll(ApiUrls.SearchIssues(), search.ToParametersDictionary()); + return ApiConnection.GetAll(ApiUrls.SearchIssues(), search.Parameters); } /// diff --git a/Octokit/GitHubClient.cs b/Octokit/GitHubClient.cs index d0967157..a1e1df7a 100644 --- a/Octokit/GitHubClient.cs +++ b/Octokit/GitHubClient.cs @@ -81,7 +81,6 @@ namespace Octokit var apiConnection = new ApiConnection(connection); Authorization = new AuthorizationsClient(apiConnection); Activity = new ActivitiesClient(apiConnection); - Blob = new BlobsClient(apiConnection); Issue = new IssuesClient(apiConnection); Miscellaneous = new MiscellaneousClient(connection); Notification = new NotificationsClient(apiConnection); @@ -132,7 +131,6 @@ namespace Octokit public IAuthorizationsClient Authorization { get; private set; } public IActivitiesClient Activity { get; private set; } - public IBlobsClient Blob { get; private set; } public IIssuesClient Issue { get; private set; } public IMiscellaneousClient Miscellaneous { get; private set; } public IOrganizationsClient Organization { get; private set; } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 1319ad24..5dd43a7e 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -729,13 +729,24 @@ namespace Octokit /// use for update or deleting a team /// /// owner of repo - /// /// name of repo + /// name of repo /// public static Uri RepoCollaborators(string owner, string repo) { return "repos/{0}/{1}/collaborators".FormatUri(owner, repo); } + /// + /// returns the for branches + /// + /// owner of repo + /// name of repo + /// + public static Uri RepoBranches(string owner, string repo) + { + return "repos/{0}/{1}/branches".FormatUri(owner, repo); + } + /// /// Creates the relative for searching repositories /// diff --git a/Octokit/Helpers/EnumExtensions.cs b/Octokit/Helpers/EnumExtensions.cs new file mode 100644 index 00000000..267196f7 --- /dev/null +++ b/Octokit/Helpers/EnumExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Linq; +using System.Reflection; +using Octokit.Internal; + +namespace Octokit +{ + static class EnumExtensions + { + public static string ToParameter(this Enum prop) + { + if (prop == null) return null; + + var member = prop.GetType().GetMember(prop.ToString()).FirstOrDefault(); + if (member == null) return null; + + var attribute = member.GetCustomAttributes(typeof(ParameterAttribute), false) + .Cast() + .FirstOrDefault(); + + return attribute != null ? attribute.Value : null; + } + } +} diff --git a/Octokit/Helpers/IApiPagination.cs b/Octokit/Helpers/IApiPagination.cs index 024d502b..1021fcc7 100644 --- a/Octokit/Helpers/IApiPagination.cs +++ b/Octokit/Helpers/IApiPagination.cs @@ -14,6 +14,6 @@ namespace Octokit /// public interface IApiPagination { - Task> GetAllPages(Func>> getFirstPage); + Task> GetAllPages(Func>> getFirstPage, Uri uri); } } \ No newline at end of file diff --git a/Octokit/Http/ApiConnection.cs b/Octokit/Http/ApiConnection.cs index 25afe422..c24baea4 100644 --- a/Octokit/Http/ApiConnection.cs +++ b/Octokit/Http/ApiConnection.cs @@ -110,7 +110,7 @@ namespace Octokit Ensure.ArgumentNotNull(uri, "uri"); return _pagination.GetAllPages(async () => await GetPage(uri, parameters, accepts) - .ConfigureAwait(false)); + .ConfigureAwait(false), uri); } /// diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index a330736c..d8366577 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -36,6 +36,21 @@ namespace Octokit { } + /// + /// 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 client to use for executing requests + /// + public Connection(ProductHeaderValue productInformation, IHttpClient httpClient) + : this(productInformation, _defaultGitHubApiUrl, _anonymousCredentials, httpClient, new SimpleJsonSerializer()) + { + } + /// /// Creates a new connection instance used to make requests of the GitHub API. /// diff --git a/Octokit/Http/HttpClientAdapter.cs b/Octokit/Http/HttpClientAdapter.cs index 5540c676..1656b023 100644 --- a/Octokit/Http/HttpClientAdapter.cs +++ b/Octokit/Http/HttpClientAdapter.cs @@ -18,6 +18,15 @@ namespace Octokit.Internal /// public class HttpClientAdapter : IHttpClient { + readonly IWebProxy webProxy; + + public HttpClientAdapter() { } + + public HttpClientAdapter(IWebProxy webProxy) + { + this.webProxy = webProxy; + } + public async Task> Send(IRequest request) { Ensure.ArgumentNotNull(request, "request"); @@ -30,6 +39,12 @@ namespace Octokit.Internal { httpOptions.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; } + if (httpOptions.SupportsProxy && webProxy != null) + { + httpOptions.UseProxy = true; + httpOptions.Proxy = webProxy; + } + var http = new HttpClient(httpOptions) { diff --git a/Octokit/IGitHubClient.cs b/Octokit/IGitHubClient.cs index 6dcedb17..80682e38 100644 --- a/Octokit/IGitHubClient.cs +++ b/Octokit/IGitHubClient.cs @@ -11,7 +11,6 @@ namespace Octokit IAuthorizationsClient Authorization { get; } IActivitiesClient Activity { get; } - IBlobsClient Blob { get; } IIssuesClient Issue { get; } IMiscellaneousClient Miscellaneous { get; } IOrganizationsClient Organization { get; } diff --git a/Octokit/Models/Request/BaseSearchRequest.cs b/Octokit/Models/Request/BaseSearchRequest.cs new file mode 100644 index 00000000..cb7392f8 --- /dev/null +++ b/Octokit/Models/Request/BaseSearchRequest.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace Octokit +{ + /// + /// Base class for searching issues/code/users/repos + /// + [SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors")] + public abstract class BaseSearchRequest + { + public BaseSearchRequest(string term) + { + Ensure.ArgumentNotNullOrEmptyString(term, "term"); + Term = term; + Page = 1; + PerPage = 100; + Order = SortDirection.Descending; + } + + /// + /// The search term + /// + public string Term { get; private set; } + + /// + /// The sort field + /// + public abstract string Sort + { + get; + } + + private string SortOrder + { + get + { + return Order.ToParameter(); + } + } + + /// + /// Optional Sort order if sort parameter is provided. One of asc or desc; the default is desc. + /// + public SortDirection Order { get; set; } + + /// + /// Page of paginated results + /// + public int Page { get; set; } + + /// + /// Number of items per page + /// + public int PerPage { get; set; } + + /// + /// All qualifiers that are used for this search + /// + public abstract IReadOnlyCollection MergedQualifiers(); + + /// + /// Add qualifiers onto the search term + /// + private string TermAndQualifiers + { + get + { + var mergedParameters = String.Join("+", MergedQualifiers()); + return Term + (mergedParameters.IsNotBlank() ? "+" + mergedParameters : ""); + } + } + + /// + /// Get the query parameters that will be appending onto the search + /// + public IDictionary Parameters + { + get + { + var d = new Dictionary(); + d.Add("page", Page.ToString(CultureInfo.CurrentCulture)); + d.Add("per_page", PerPage.ToString(CultureInfo.CurrentCulture)); + d.Add("sort", Sort); + d.Add("order", SortOrder); + d.Add("q", TermAndQualifiers); + return d; + } + } + } +} \ No newline at end of file diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index 346917c6..1449569f 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -4,49 +4,299 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Globalization; +using System.Diagnostics.CodeAnalysis; namespace Octokit { /// /// Searching Issues /// - public class SearchIssuesRequest : RequestParameters + public class SearchIssuesRequest : BaseSearchRequest { - public SearchIssuesRequest(string term) + public SearchIssuesRequest(string term) : base(term) { } + + /// + /// Optional Sort field. One of comments, created, or updated. + /// If not provided, results are sorted by best match. + /// + /// + /// http://developer.github.com/v3/search/#search-issues + /// + public IssueSearchSort? SortField { get; set; } + + public override string Sort { - Ensure.ArgumentNotNullOrEmptyString(term,"term"); - Term = term; - Page = 1; - PerPage = 100; - Order = SortDirection.Descending; + get { return SortField.ToParameter(); } } /// - /// The search terms. This can be any combination of the supported repository search parameters: - /// http://developer.github.com/v3/search/#search-code + /// Qualifies which fields are searched. With this qualifier you can restrict + /// the search to just the title, body, comments, or any combination of these. /// - [Parameter(Key= "q")] - public string Term { get; private set; } + /// + /// https://help.github.com/articles/searching-issues#search-in + /// + private IEnumerable _inQualifier; + public IEnumerable In + { + get { return _inQualifier; } + set + { + if (value != null && value.Any()) + { + _inQualifier = value.Distinct().ToList(); + } + } + } /// - /// For http://developer.github.com/v3/search/#search-issues - /// Optional Sort field. One of comments, created, or updated. If not provided, results are sorted by best match. + /// With this qualifier you can restrict the search to issues or pull request only. /// - //public string Sort { get; set; } //re-add laters + /// + /// https://help.github.com/articles/searching-issues#type + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public IssueTypeQualifier? Type { get; set; } /// - /// Optional Sort order if sort parameter is provided. One of asc or desc; the default is desc. + /// Finds issues created by a certain user. /// - public SortDirection Order { get; set; } + /// + /// https://help.github.com/articles/searching-issues#author + /// + public string Author { get; set; } /// - /// Page of paginated results + /// Finds issues that are assigned to a certain user. /// - public int Page { get; set; } + /// + /// https://help.github.com/articles/searching-issues#assignee + /// + public string Assignee { get; set; } /// - /// Number of items per page + /// Finds issues that mention a certain user. /// - public int PerPage { get; set; } + /// + /// https://help.github.com/articles/searching-issues#mentions + /// + public string Mentions { get; set; } + + /// + /// Finds issues that a certain user commented on. + /// + /// + /// https://help.github.com/articles/searching-issues#commenter + /// + public string Commenter { get; set; } + + /// + /// Finds issues that were either created by a certain user, assigned to that user, + /// mention that user, or were commented on by that user. + /// + /// + /// https://help.github.com/articles/searching-issues#involves + /// + public string Involves { get; set; } + + /// + /// Filter issues based on whether they’re open or closed. + /// + /// + /// https://help.github.com/articles/searching-issues#state + /// + public ItemState? State { get; set; } + + private IEnumerable _labels; + /// + /// Filters issues based on their labels. + /// + /// + /// https://help.github.com/articles/searching-issues#labels + /// + public IEnumerable Labels + { + get { return _labels; } + set + { + if (value != null && value.Any()) + { + _labels = value.Distinct().ToList(); + } + } + } + + /// + /// Searches for issues within repositories that match a certain language. + /// + /// + /// https://help.github.com/articles/searching-issues#language + /// + public Language? Language { get; set; } + + /// + /// Filters issues based on times of creation. + /// + /// + /// https://help.github.com/articles/searching-issues#created-and-last-updated + /// + public DateRange Created { get; set; } + + /// + /// Filters issues based on times when they were last updated. + /// + /// + /// https://help.github.com/articles/searching-issues#created-and-last-updated + /// + public DateRange Updated { get; set; } + + /// + /// Filters issues based on the quantity of comments. + /// + /// + /// https://help.github.com/articles/searching-issues#comments + /// + public Range Comments { get; set; } + + /// + /// Limits searches to a specific user. + /// + /// + /// https://help.github.com/articles/searching-issues#users-organizations-and-repositories + /// + public string User { get; set; } + + /// + /// Limits searches to a specific repository. + /// + /// + /// https://help.github.com/articles/searching-issues#users-organizations-and-repositories + /// + public string Repo { get; set; } + + public override IReadOnlyCollection MergedQualifiers() + { + var parameters = new List(); + + if (In != null) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "in:{0}", + String.Join(",", In.Select(i => i.ToParameter())))); + } + + if (Type != null) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "type:{0}", + Type.ToParameter())); + } + + if (Author.IsNotBlank()) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "author:{0}", Author)); + } + + if (Assignee.IsNotBlank()) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "assignee:{0}", Assignee)); + } + + if (Mentions.IsNotBlank()) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "mentions:{0}", Mentions)); + } + + if (Commenter.IsNotBlank()) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "commenter:{0}", Commenter)); + } + + if (Involves.IsNotBlank()) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "involves:{0}", Involves)); + } + + if (State != null) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "state:{0}", State)); + } + + if (Labels != null) + { + foreach (var label in Labels) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "label:{0}", label)); + } + } + + if (Language != null) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "language:{0}", Language)); + } + + if (Created != null) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "created:{0}", Created)); + } + + if (Updated != null) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "updated:{0}", Updated)); + } + + if (Comments != null) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "comments:{0}", Comments)); + } + + if (User.IsNotBlank()) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "user:{0}", User)); + } + + if (Repo.IsNotBlank()) + { + parameters.Add(String.Format(CultureInfo.InvariantCulture, "repo:{0}", Repo)); + } + + return parameters; + } + } + + public enum IssueSearchSort + { + /// + /// search by number of comments + /// + [Parameter(Value = "comments")] + Comments, + /// + /// search by created + /// + [Parameter(Value = "created")] + Created, + /// + /// search by last updated + /// + [Parameter(Value = "updated")] + Updated + } + + public enum IssueInQualifier + { + [Parameter(Value = "title")] + Title, + [Parameter(Value = "body")] + Body, + [Parameter(Value = "comment")] + Comment + } + + public enum IssueTypeQualifier + { + [Parameter(Value = "pr")] + PR, + [Parameter(Value = "issue")] + Issue } } diff --git a/Octokit/Models/Request/SearchRepositoriesRequest.cs b/Octokit/Models/Request/SearchRepositoriesRequest.cs index 5a00eb37..01ab6370 100644 --- a/Octokit/Models/Request/SearchRepositoriesRequest.cs +++ b/Octokit/Models/Request/SearchRepositoriesRequest.cs @@ -308,16 +308,16 @@ namespace Octokit switch (op) { case SearchQualifierOperator.GreaterThan: - query = string.Format(">{0}", date.ToString("yyyy-mm-dd")); + query = string.Format(">{0}", date.ToString("yyyy-MM-dd")); break; case SearchQualifierOperator.LessThan: - query = string.Format("<{0}", date.ToString("yyyy-mm-dd")); + query = string.Format("<{0}", date.ToString("yyyy-MM-dd")); break; case SearchQualifierOperator.LessThanOrEqualTo: - query = string.Format("<={0}", date.ToString("yyyy-mm-dd")); + query = string.Format("<={0}", date.ToString("yyyy-MM-dd")); break; case SearchQualifierOperator.GreaterThanOrEqualTo: - query = string.Format(">={0}", date.ToString("yyyy-mm-dd")); + query = string.Format(">={0}", date.ToString("yyyy-MM-dd")); break; default: break; diff --git a/Octokit/Models/Response/Branch.cs b/Octokit/Models/Response/Branch.cs new file mode 100644 index 00000000..267a4bd4 --- /dev/null +++ b/Octokit/Models/Response/Branch.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit +{ + public class Branch + { + /// + /// Name of this . + /// + public string Name { get; set; } + + /// + /// The history for this . + /// + public GitReference Commit { get; set; } + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 61dd2346..522b4bfb 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -98,6 +98,7 @@ + @@ -245,6 +246,8 @@ + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index ccf38838..932a66d0 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -255,6 +255,9 @@ + + + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 6357004a..a9f1d577 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -250,6 +250,9 @@ + + + \ No newline at end of file diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 8d4d829b..bb077574 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -117,6 +117,7 @@ + @@ -189,6 +190,7 @@ + @@ -243,6 +245,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index f921aec7..febcad07 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -56,6 +56,10 @@ + + + Code + @@ -87,6 +91,7 @@ + @@ -274,7 +279,7 @@ - +