From a29752d363df4e9e557fa8dfe11ea7bda30a8a29 Mon Sep 17 00:00:00 2001 From: Alexander Efremov Date: Wed, 1 Jun 2016 21:12:33 +0700 Subject: [PATCH 1/6] Add ApiOption overloads to methods on I(Observable)OrganizationsClient (#1324) --- .../Clients/IObservableOrganizationsClient.cs | 17 ++ .../Clients/ObservableOrganizationsClient.cs | 29 ++++ .../Clients/OrganizationClientTests.cs | 124 ++++++++++++++ .../Octokit.Tests.Integration.csproj | 1 + .../Clients/OrganizationsClientTests.cs | 113 ++++++++++--- Octokit.Tests/Octokit.Tests.csproj | 1 + .../ObservableOrganizationsClientTests.cs | 157 ++++++++++++++++++ Octokit/Clients/IOrganizationsClient.cs | 19 +++ Octokit/Clients/OrganizationsClient.cs | 33 +++- 9 files changed, 468 insertions(+), 26 deletions(-) create mode 100644 Octokit.Tests.Integration/Clients/OrganizationClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableOrganizationsClientTests.cs diff --git a/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs b/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs index f36bb44a..fb9efd7f 100644 --- a/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs +++ b/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs @@ -32,6 +32,15 @@ namespace Octokit.Reactive Justification = "Method makes a network request")] IObservable GetAllForCurrent(); + /// + /// Returns all the organizations for the current user. + /// + /// Options for changing the API response + /// + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "Method makes a network request")] + IObservable GetAllForCurrent(ApiOptions options); + /// /// Returns all the organizations for the specified user /// @@ -39,6 +48,14 @@ namespace Octokit.Reactive /// IObservable GetAll(string user); + /// + /// Returns all the organizations for the specified user + /// + /// The login for the user + /// Options for changing the API response + /// + IObservable GetAll(string user, ApiOptions options); + /// /// Update the specified organization with data from . /// diff --git a/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs index 64afff9d..ff5b3101 100644 --- a/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs +++ b/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs @@ -52,6 +52,18 @@ namespace Octokit.Reactive /// public IObservable GetAllForCurrent() { + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// Returns all the organizations for the current user. + /// + /// Options for changing the API response + /// + public IObservable GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + return _connection.GetAndFlattenAllPages(ApiUrls.Organizations()); } @@ -67,6 +79,20 @@ namespace Octokit.Reactive return _connection.GetAndFlattenAllPages(ApiUrls.Organizations(user)); } + /// + /// Returns all the organizations for the specified user + /// + /// The login for the user + /// Options for changing the API response + /// + public IObservable GetAll(string user, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Organizations(user), options); + } + /// /// Update the specified organization with data from . /// @@ -76,6 +102,9 @@ namespace Octokit.Reactive /// A public IObservable Update(string organizationName, OrganizationUpdate updateRequest) { + Ensure.ArgumentNotNullOrEmptyString(organizationName, "organizationName"); + Ensure.ArgumentNotNull(updateRequest, "updateRequest"); + return _client.Update(organizationName, updateRequest).ToObservable(); } } diff --git a/Octokit.Tests.Integration/Clients/OrganizationClientTests.cs b/Octokit.Tests.Integration/Clients/OrganizationClientTests.cs new file mode 100644 index 00000000..5577f154 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/OrganizationClientTests.cs @@ -0,0 +1,124 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Integration.Clients +{ + public class OrganizationClientTests + { + public class TheGetAllMethod + { + readonly IGitHubClient _github; + readonly IOrganizationsClient _organizationsClient; + + public TheGetAllMethod() + { + _github = EnterpriseHelper.GetAuthenticatedClient(); + + _organizationsClient = _github.Organization; + } + + [GitHubEnterpriseTest] + public async Task CanListOrganizations() + { + string orgLogin = Helper.MakeNameWithTimestamp("MyOrganization"); + string orgName = string.Concat(orgLogin, " Display Name"); + + var newOrganization = new NewOrganization(orgLogin, EnterpriseHelper.UserName, orgName); + var organization = await + _github.Enterprise.Organization.Create(newOrganization); + + Assert.NotNull(organization); + + var milestones = await _organizationsClient.GetAllForCurrent(); + + Assert.NotEmpty(milestones); + } + + [GitHubEnterpriseTest] + public async Task ReturnsCorrectCountOfOrganizationsWithoutStart() + { + string orgLogin1 = Helper.MakeNameWithTimestamp("MyOrganization1"); + string orgName1 = string.Concat(orgLogin1, " Display Name 1"); + string orgLogin2 = Helper.MakeNameWithTimestamp("MyOrganization2"); + string orgName2 = string.Concat(orgLogin2, " Display Name 2"); + + var newOrganization1 = new NewOrganization(orgLogin1, EnterpriseHelper.UserName, orgName1); + var newOrganization2 = new NewOrganization(orgLogin2, EnterpriseHelper.UserName, orgName2); + await _github.Enterprise.Organization.Create(newOrganization1); + await _github.Enterprise.Organization.Create(newOrganization2); + + var options = new ApiOptions + { + PageSize = 2, + PageCount = 1 + }; + + var organizations = await _organizationsClient.GetAllForCurrent(options); + + Assert.Equal(2, organizations.Count); + } + + [GitHubEnterpriseTest] + public async Task ReturnsCorrectCountOfOrganizationsWithStart() + { + string orgLogin1 = Helper.MakeNameWithTimestamp("MyOrganization1"); + string orgName1 = string.Concat(orgLogin1, " Display Name 1"); + string orgLogin2 = Helper.MakeNameWithTimestamp("MyOrganization2"); + string orgName2 = string.Concat(orgLogin2, " Display Name 2"); + + var newOrganization1 = new NewOrganization(orgLogin1, EnterpriseHelper.UserName, orgName1); + var newOrganization2 = new NewOrganization(orgLogin2, EnterpriseHelper.UserName, orgName2); + await _github.Enterprise.Organization.Create(newOrganization1); + await _github.Enterprise.Organization.Create(newOrganization2); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2, + }; + + var organizations = await _organizationsClient.GetAllForCurrent(options); + + Assert.Equal(1, organizations.Count); + } + + [GitHubEnterpriseTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + string orgLogin1 = Helper.MakeNameWithTimestamp("MyOrganization1"); + string orgName1 = string.Concat(orgLogin1, " Display Name 1"); + string orgLogin2 = Helper.MakeNameWithTimestamp("MyOrganization2"); + string orgName2 = string.Concat(orgLogin2, " Display Name 2"); + string orgLogin3 = Helper.MakeNameWithTimestamp("MyOrganization3"); + string orgName3 = string.Concat(orgLogin3, " Display Name 3"); + + var newOrganization1 = new NewOrganization(orgLogin1, EnterpriseHelper.UserName, orgName1); + var newOrganization2 = new NewOrganization(orgLogin2, EnterpriseHelper.UserName, orgName2); + var newOrganization3 = new NewOrganization(orgLogin3, EnterpriseHelper.UserName, orgName3); + await _github.Enterprise.Organization.Create(newOrganization1); + await _github.Enterprise.Organization.Create(newOrganization2); + await _github.Enterprise.Organization.Create(newOrganization3); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _organizationsClient.GetAllForCurrent(startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _organizationsClient.GetAllForCurrent(skipStartOptions); + + Assert.NotEqual(firstPage[0].Login, secondPage[0].Login); + } + } + } +} diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index d5adfaaf..be61279e 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -94,6 +94,7 @@ + diff --git a/Octokit.Tests/Clients/OrganizationsClientTests.cs b/Octokit.Tests/Clients/OrganizationsClientTests.cs index 518de8cc..ec5b1584 100644 --- a/Octokit.Tests/Clients/OrganizationsClientTests.cs +++ b/Octokit.Tests/Clients/OrganizationsClientTests.cs @@ -23,72 +23,137 @@ namespace Octokit.Tests.Clients public class TheGetMethod { [Fact] - public void RequestsCorrectUrl() + public async Task RequestsCorrectUrl() { - var client = Substitute.For(); - var orgsClient = new OrganizationsClient(client); + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); - orgsClient.Get("orgName"); + await client.Get("orgName"); - client.Received().Get(Arg.Is(u => u.ToString() == "orgs/orgName")); + connection.Received().Get(Arg.Is(u => u.ToString() == "orgs/orgName")); } [Fact] public async Task EnsuresNonNullArguments() { - var orgs = new OrganizationsClient(Substitute.For()); + var client = new OrganizationsClient(Substitute.For()); - await Assert.ThrowsAsync(() => orgs.Get(null)); + await Assert.ThrowsAsync(() => client.Get(null)); + + await Assert.ThrowsAsync(() => client.Get("")); } } public class TheGetAllMethod { [Fact] - public void RequestsTheCorrectUrl() + public async Task RequestsTheCorrectUrl() { - var client = Substitute.For(); - var orgs = new OrganizationsClient(client); + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); - orgs.GetAll("username"); + await client.GetAll("username"); - client.Received().GetAll(Arg.Is(u => u.ToString() == "users/username/orgs")); + connection.Received().GetAll(Arg.Is(u => u.ToString() == "users/username/orgs"), Args.ApiOptions); + } + + [Fact] + public async Task RequestsTheCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + await client.GetAll("username", options); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "users/username/orgs"), options); } [Fact] public async Task EnsuresNonNullArguments() { - var orgs = new OrganizationsClient(Substitute.For()); + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); - await Assert.ThrowsAsync(() => orgs.GetAll(null)); + await Assert.ThrowsAsync(() => client.GetAll(null)); + await Assert.ThrowsAsync(() => client.GetAll(null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAll("username", null)); + + await Assert.ThrowsAsync(() => client.GetAll("")); + await Assert.ThrowsAsync(() => client.GetAll("", ApiOptions.None)); } } public class TheGetAllForCurrentMethod { [Fact] - public void RequestsTheCorrectUrl() + public async Task RequestsTheCorrectUrl() { - var client = Substitute.For(); - var orgs = new OrganizationsClient(client); + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); - orgs.GetAllForCurrent(); + await client.GetAllForCurrent(); - client.Received().GetAll(Arg.Is(u => u.ToString() == "user/orgs")); + connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/orgs"), Args.ApiOptions); + } + + [Fact] + public async Task RequestsTheCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + await client.GetAllForCurrent(options); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/orgs"), options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); + + await Assert.ThrowsAsync(() => client.GetAllForCurrent(null)); } } public class TheUpdateMethod { [Fact] - public void RequestsTheCorrectUrl() + public async Task RequestsTheCorrectUrl() { - var client = Substitute.For(); - var orgs = new OrganizationsClient(client); + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); - orgs.Update("initrode", new OrganizationUpdate()); + await client.Update("initrode", new OrganizationUpdate()); - client.Received().Patch(Arg.Is(u => u.ToString() == "orgs/initrode"), Args.OrganizationUpdate); + connection.Received().Patch(Arg.Is(u => u.ToString() == "orgs/initrode"), Args.OrganizationUpdate); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new OrganizationsClient(connection); + + await Assert.ThrowsAsync(() => client.Update(null, new OrganizationUpdate())); + await Assert.ThrowsAsync(() => client.Update("org", null)); + + await Assert.ThrowsAsync(() => client.Update("", new OrganizationUpdate())); } } } diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 87cd858a..5e78e372 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -207,6 +207,7 @@ + diff --git a/Octokit.Tests/Reactive/ObservableOrganizationsClientTests.cs b/Octokit.Tests/Reactive/ObservableOrganizationsClientTests.cs new file mode 100644 index 00000000..4889a4e5 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableOrganizationsClientTests.cs @@ -0,0 +1,157 @@ +using System; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableOrganizationsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableOrganizationsClient(null)); + } + } + + public class TheGetMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + client.Get("orgName"); + + gitHubClient.Received().Organization.Get("orgName"); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableOrganizationsClient(Substitute.For()); + + Assert.Throws(() => client.Get(null)); + + Assert.Throws(() => client.Get("")); + } + } + + public class TheGetAllMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + client.GetAll("username"); + + gitHubClient.Received().Organization.GetAll("username"); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + client.GetAll("username", options); + + gitHubClient.Received().Organization.GetAll("username", options); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + Assert.Throws(() => client.GetAll(null)); + Assert.Throws(() => client.GetAll(null, ApiOptions.None)); + Assert.Throws(() => client.GetAll("username", null)); + + Assert.Throws(() => client.GetAll("")); + Assert.Throws(() => client.GetAll("", ApiOptions.None)); + } + } + + public class TheGetAllForCurrentMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + client.GetAllForCurrent(); + + gitHubClient.Received().Organization.GetAllForCurrent(); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + var options = new ApiOptions + { + StartPage = 1, + PageCount = 1, + PageSize = 1 + }; + + client.GetAllForCurrent(options); + + gitHubClient.Received().Organization.GetAllForCurrent(options); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + Assert.Throws(() => client.GetAllForCurrent(null)); + } + } + + public class TheUpdateMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + var organizationUpdate = new OrganizationUpdate(); + client.Update("initrode", organizationUpdate); + + gitHubClient.Received().Organization.Update("initrode", organizationUpdate); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationsClient(gitHubClient); + + Assert.Throws(() => client.Update(null, new OrganizationUpdate())); + Assert.Throws(() => client.Update("org", null)); + + Assert.Throws(() => client.Update("", new OrganizationUpdate())); + } + } + } +} \ No newline at end of file diff --git a/Octokit/Clients/IOrganizationsClient.cs b/Octokit/Clients/IOrganizationsClient.cs index 9d5cf337..fea9ab0a 100644 --- a/Octokit/Clients/IOrganizationsClient.cs +++ b/Octokit/Clients/IOrganizationsClient.cs @@ -43,6 +43,16 @@ namespace Octokit Justification = "Method makes a network request")] Task> GetAllForCurrent(); + /// + /// Returns all s for the current user. + /// + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// A list of the current user's s. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "Method makes a network request")] + Task> GetAllForCurrent(ApiOptions options); + /// /// Returns all s for the specified user. /// @@ -50,6 +60,15 @@ namespace Octokit /// A list of the specified user's s. Task> GetAll(string user); + /// + /// Returns all s for the specified user. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// A list of the specified user's s. + Task> GetAll(string user, ApiOptions options); + /// /// Update the specified organization with data from . /// diff --git a/Octokit/Clients/OrganizationsClient.cs b/Octokit/Clients/OrganizationsClient.cs index 113247c1..32fa827c 100644 --- a/Octokit/Clients/OrganizationsClient.cs +++ b/Octokit/Clients/OrganizationsClient.cs @@ -54,19 +54,48 @@ namespace Octokit /// A list of the current user's s. public Task> GetAllForCurrent() { - return ApiConnection.GetAll(ApiUrls.Organizations()); + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// Returns all s for the current user. + /// + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// A list of the current user's s. + public Task> GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Organizations(), options); } /// /// Returns all s for the specified user. /// + /// The login of the user /// Thrown when a general API error occurs. /// A list of the specified user's s. public Task> GetAll(string user) { Ensure.ArgumentNotNullOrEmptyString(user, "user"); - return ApiConnection.GetAll(ApiUrls.Organizations(user)); + return GetAll(user, ApiOptions.None); + } + + /// + /// Returns all s for the specified user. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// A list of the specified user's s. + public Task> GetAll(string user, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Organizations(user), options); } /// From 437bf911173b3da202b1687ae18e89ba9a4bb29d Mon Sep 17 00:00:00 2001 From: Alexander Efremov Date: Wed, 1 Jun 2016 21:16:55 +0700 Subject: [PATCH 2/6] Add ApiOptions overloads to methods on I(Observable)OrganizationMembersClient (#1332) --- .../IObservableOrganizationMembersClient.cs | 105 ++++++++++++ .../ObservableOrganizationMembersClient.cs | 145 ++++++++++++++++- .../Clients/OrganizationMembersClientTests.cs | 60 ++++++- .../Clients/OrganizationMembersClientTests.cs | 153 +++++++++++++++--- ...bservableOrganizationMembersClientTests.cs | 126 ++++++++++++++- Octokit/Clients/IOrganizationMembersClient.cs | 108 ++++++++++++- Octokit/Clients/OrganizationMembersClient.cs | 148 ++++++++++++++++- 7 files changed, 802 insertions(+), 43 deletions(-) diff --git a/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs b/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs index 5d15d246..8d77cad6 100644 --- a/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs +++ b/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs @@ -27,6 +27,29 @@ namespace Octokit.Reactive /// IObservable GetAll(string org); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// Options for changing the API response + /// + IObservable GetAll(string org, ApiOptions options); + /// /// /// List all users who are members of an organization. A member is a user that @@ -50,6 +73,30 @@ namespace Octokit.Reactive /// IObservable GetAll(string org, OrganizationMembersFilter filter); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The members filter, + /// Options for changing the API response + /// + IObservable GetAll(string org, OrganizationMembersFilter filter, ApiOptions options); + /// /// /// List all users who are members of an organization. A member is a user that @@ -73,6 +120,30 @@ namespace Octokit.Reactive /// IObservable GetAll(string org, OrganizationMembersRole role); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The role filter to use when getting the users, + /// Options for changing the API response + /// + IObservable GetAll(string org, OrganizationMembersRole role, ApiOptions options); + /// /// /// List all users who are members of an organization. A member is a user that @@ -97,6 +168,31 @@ namespace Octokit.Reactive /// IObservable GetAll(string org, OrganizationMembersFilter filter, OrganizationMembersRole role); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The members filter, + /// The role filter to use when getting the users, + /// Options for changing the API response + /// + IObservable GetAll(string org, OrganizationMembersFilter filter, OrganizationMembersRole role, ApiOptions options); + /// /// List all users who have publicized their membership of the organization. /// @@ -105,6 +201,15 @@ namespace Octokit.Reactive /// IObservable GetAllPublic(string org); + /// + /// List all users who have publicized their membership of the organization. + /// + /// http://developer.github.com/v3/orgs/members/#public-members-list + /// The login for the organization + /// Options for changing the API response + /// + IObservable GetAllPublic(string org, ApiOptions options); + /// /// Check if a user is, publicly or privately, a member of the organization. /// diff --git a/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs index 0bb84d35..56bde28b 100644 --- a/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs +++ b/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs @@ -46,7 +46,36 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return _connection.GetAndFlattenAllPages(ApiUrls.Members(org)); + return GetAll(org, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// Options for changing the API response + /// + public IObservable GetAll(string org, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Members(org), options); } /// @@ -74,7 +103,37 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return _connection.GetAndFlattenAllPages(ApiUrls.Members(org, filter)); + return GetAll(org, filter, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The members filter, + /// Options for changing the API response + /// + public IObservable GetAll(string org, OrganizationMembersFilter filter, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Members(org, filter), options); } /// @@ -102,7 +161,37 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return _connection.GetAndFlattenAllPages(ApiUrls.Members(org, role)); + return GetAll(org, role, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The role filter to use when getting the users, + /// Options for changing the API response + /// + public IObservable GetAll(string org, OrganizationMembersRole role, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Members(org, role), options); } /// @@ -131,7 +220,38 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return _connection.GetAndFlattenAllPages(ApiUrls.Members(org, filter, role)); + return GetAll(org, filter, role, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The members filter, + /// The role filter to use when getting the users, + /// Options for changing the API response + /// + public IObservable GetAll(string org, OrganizationMembersFilter filter, OrganizationMembersRole role, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Members(org, filter, role), options); } /// @@ -144,7 +264,22 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return _connection.GetAndFlattenAllPages(ApiUrls.PublicMembers(org)); + return GetAllPublic(org, ApiOptions.None); + } + + /// + /// List all users who have publicized their membership of the organization. + /// + /// http://developer.github.com/v3/orgs/members/#public-members-list + /// The login for the organization + /// Options for changing the API response + /// + public IObservable GetAllPublic(string org, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.PublicMembers(org), options); } /// diff --git a/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs b/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs index d0cb22b9..5e3d4c56 100644 --- a/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs +++ b/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs @@ -20,8 +20,64 @@ namespace Octokit.Tests.Integration.Clients [IntegrationTest] public async Task ReturnsMembers() { - var members = _gitHub.Organization.Member.GetAll(_organizationFixture); - Assert.NotNull(members); + var members = await + _gitHub.Organization.Member.GetAll(_organizationFixture); + Assert.NotEmpty(members); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfMembersWithoutStart() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var members = await _gitHub.Organization.Member.GetAll(_organizationFixture, options); + + Assert.Equal(1, members.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfMembersWithStart() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var members = await _gitHub.Organization.Member.GetAll(_organizationFixture, options); + + Assert.Equal(1, members.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctMembersBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _gitHub.Organization.Member.GetAll(_organizationFixture, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _gitHub.Organization.Member.GetAll(_organizationFixture, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); } [IntegrationTest(Skip = "TwoFactor filter can't be used unless the requester is an organization owner")] diff --git a/Octokit.Tests/Clients/OrganizationMembersClientTests.cs b/Octokit.Tests/Clients/OrganizationMembersClientTests.cs index 4bf1b353..bcd2ec4f 100644 --- a/Octokit.Tests/Clients/OrganizationMembersClientTests.cs +++ b/Octokit.Tests/Clients/OrganizationMembersClientTests.cs @@ -33,16 +33,51 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org"); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members"), Args.ApiOptions); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembersClient = new OrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + orgMembersClient.GetAll("org", options); + + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members"), options); } [Fact] public async Task EnsureNonNullArguments() { - var orgMembers = new OrganizationMembersClient(Substitute.For()); + var client = new OrganizationMembersClient(Substitute.For()); - await Assert.ThrowsAsync(() => orgMembers.GetAll(null)); - await Assert.ThrowsAsync(() => orgMembers.GetAll("")); + await Assert.ThrowsAsync(() => client.GetAll(null)); + + await Assert.ThrowsAsync(() => client.GetAll(null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAll("org", null)); + + await Assert.ThrowsAsync(() => client.GetAll(null, OrganizationMembersFilter.All)); + await Assert.ThrowsAsync(() => client.GetAll(null, OrganizationMembersFilter.All, ApiOptions.None)); + + await Assert.ThrowsAsync(() => client.GetAll("org", OrganizationMembersFilter.All, null)); + await Assert.ThrowsAsync(() => client.GetAll(null, OrganizationMembersFilter.All, OrganizationMembersRole.Admin)); + + await Assert.ThrowsAsync(() => client.GetAll(null, OrganizationMembersFilter.All, OrganizationMembersRole.Admin, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAll("org", OrganizationMembersFilter.All, OrganizationMembersRole.Admin, null)); + + await Assert.ThrowsAsync(() => client.GetAll("")); + await Assert.ThrowsAsync(() => client.GetAll("", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAll("", OrganizationMembersFilter.All)); + await Assert.ThrowsAsync(() => client.GetAll("", OrganizationMembersFilter.All, OrganizationMembersRole.Admin)); + await Assert.ThrowsAsync(() => client.GetAll("", OrganizationMembersFilter.All, OrganizationMembersRole.Admin, ApiOptions.None)); } [Fact] @@ -53,7 +88,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.All); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all"), Args.ApiOptions); } [Fact] @@ -64,7 +99,25 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled"), Args.ApiOptions); + } + + [Fact] + public void TwoFactorFilterRequestTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembersClient = new OrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled, options); + + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled"), options); } [Fact] @@ -75,7 +128,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersRole.All); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?role=all")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?role=all"), Args.ApiOptions); } [Fact] @@ -86,7 +139,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersRole.Admin); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?role=admin")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?role=admin"), Args.ApiOptions); } [Fact] @@ -97,7 +150,25 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersRole.Member); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?role=member")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?role=member"), Args.ApiOptions); + } + + [Fact] + public void MemberRoleFilterRequestTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembersClient = new OrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + orgMembersClient.GetAll("org", OrganizationMembersRole.Member, options); + + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?role=member"), options); } [Fact] @@ -108,7 +179,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.All, OrganizationMembersRole.All); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all&role=all")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all&role=all"), Args.ApiOptions); } [Fact] @@ -119,7 +190,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.All, OrganizationMembersRole.Admin); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all&role=admin")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all&role=admin"), Args.ApiOptions); } [Fact] @@ -130,7 +201,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.All, OrganizationMembersRole.Member); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all&role=member")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=all&role=member"), Args.ApiOptions); } [Fact] @@ -141,7 +212,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled, OrganizationMembersRole.All); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=all")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=all"), Args.ApiOptions); } [Fact] @@ -152,7 +223,7 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled, OrganizationMembersRole.Admin); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=admin")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=admin"), Args.ApiOptions); } [Fact] @@ -163,30 +234,70 @@ namespace Octokit.Tests.Clients orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled, OrganizationMembersRole.Member); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=member")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=member"), Args.ApiOptions); + } + + [Fact] + public void TwoFactorFilterPlusMemberRoleRequestTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembersClient = new OrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled, OrganizationMembersRole.Member, options); + + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=member"), options); } } public class TheGetPublicMethod { [Fact] - public void RequestsTheCorrectUrl() + public async Task RequestsTheCorrectUrl() { var client = Substitute.For(); var orgMembers = new OrganizationMembersClient(client); - orgMembers.GetAllPublic("org"); + await orgMembers.GetAllPublic("org"); - client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/public_members")); + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/public_members"), Args.ApiOptions); + } + + [Fact] + public async Task RequestsTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembers = new OrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + await orgMembers.GetAllPublic("org", options); + + client.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/public_members"), options); } [Fact] public async Task EnsureNonNullArguments() { - var orgMembers = new OrganizationMembersClient(Substitute.For()); + var client = new OrganizationMembersClient(Substitute.For()); - await Assert.ThrowsAsync(() => orgMembers.GetAllPublic(null)); - await Assert.ThrowsAsync(() => orgMembers.GetAllPublic("")); + await Assert.ThrowsAsync(() => client.GetAllPublic(null)); + await Assert.ThrowsAsync(() => client.GetAllPublic(null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllPublic("org", null)); + + await Assert.ThrowsAsync(() => client.GetAllPublic("")); + await Assert.ThrowsAsync(() => client.GetAllPublic("", ApiOptions.None)); } } diff --git a/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs b/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs index 92941de0..6f4dc49c 100644 --- a/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs @@ -31,7 +31,26 @@ namespace Octokit.Tests.Reactive client.GetAll("org"); gitHubClient.Connection.Received(1).Get>( - new Uri("orgs/org/members", UriKind.Relative), null, null); + new Uri("orgs/org/members", UriKind.Relative), Args.EmptyDictionary, null); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAll("org", options); + + gitHubClient.Connection.Received(1).Get>( + new Uri("orgs/org/members", UriKind.Relative), Arg.Is>(d => d.Count == 2), null); } [Fact] @@ -39,8 +58,80 @@ namespace Octokit.Tests.Reactive { var client = new ObservableOrganizationMembersClient(Substitute.For()); - await Assert.ThrowsAsync(() => client.GetAll(null).ToTask()); - await Assert.ThrowsAsync(() => client.GetAll("").ToTask()); + Assert.Throws(() => client.GetAll(null)); + + Assert.Throws(() => client.GetAll(null, ApiOptions.None)); + Assert.Throws(() => client.GetAll("org", null)); + + Assert.Throws(() => client.GetAll(null, OrganizationMembersFilter.All)); + Assert.Throws(() => client.GetAll(null, OrganizationMembersFilter.All, ApiOptions.None)); + + Assert.Throws(() => client.GetAll("org", OrganizationMembersFilter.All, null)); + Assert.Throws(() => client.GetAll(null, OrganizationMembersFilter.All, OrganizationMembersRole.Admin)); + + Assert.Throws(() => client.GetAll(null, OrganizationMembersFilter.All, OrganizationMembersRole.Admin, ApiOptions.None)); + Assert.Throws(() => client.GetAll("org", OrganizationMembersFilter.All, OrganizationMembersRole.Admin, null)); + + Assert.Throws(() => client.GetAll("")); + Assert.Throws(() => client.GetAll("", ApiOptions.None)); + Assert.Throws(() => client.GetAll("", OrganizationMembersFilter.All)); + Assert.Throws(() => client.GetAll("", OrganizationMembersFilter.All, OrganizationMembersRole.Admin)); + Assert.Throws(() => client.GetAll("", OrganizationMembersFilter.All, OrganizationMembersRole.Admin, ApiOptions.None)); + } + + [Fact] + public void TwoFactorFilterRequestTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembersClient = new ObservableOrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled, options); + + client.Connection.Received(1).Get>( + new Uri("orgs/org/members?filter=2fa_disabled", UriKind.Relative), Arg.Is>(d => d.Count == 2), null); + } + + [Fact] + public void MemberRoleFilterRequestTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembersClient = new ObservableOrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + orgMembersClient.GetAll("org", OrganizationMembersRole.Member, options); + + client.Connection.Received().Get>(Arg.Is(u => u.ToString() == "orgs/org/members?role=member"), Arg.Is>(d => d.Count == 2), null); + } + + [Fact] + public void TwoFactorFilterPlusMemberRoleRequestTheCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var orgMembersClient = new ObservableOrganizationMembersClient(client); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + orgMembersClient.GetAll("org", OrganizationMembersFilter.TwoFactorAuthenticationDisabled, OrganizationMembersRole.Member, options); + + client.Connection.Received().Get>(Arg.Is(u => u.ToString() == "orgs/org/members?filter=2fa_disabled&role=member"), Arg.Is>(d => d.Count == 2), null); } } @@ -55,7 +146,26 @@ namespace Octokit.Tests.Reactive client.GetAllPublic("org"); gitHubClient.Connection.Received(1).Get>( - new Uri("orgs/org/public_members", UriKind.Relative), null, null); + new Uri("orgs/org/public_members", UriKind.Relative), Args.EmptyDictionary, null); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllPublic("org", options); + + gitHubClient.Connection.Received(1).Get>( + new Uri("orgs/org/public_members", UriKind.Relative), Arg.Is>(d => d.Count == 2), null); } [Fact] @@ -63,8 +173,12 @@ namespace Octokit.Tests.Reactive { var client = new ObservableOrganizationMembersClient(Substitute.For()); - await Assert.ThrowsAsync(() => client.GetAllPublic(null).ToTask()); - await Assert.ThrowsAsync(() => client.GetAllPublic("").ToTask()); + Assert.Throws(() => client.GetAllPublic(null)); + Assert.Throws(() => client.GetAllPublic(null, ApiOptions.None)); + Assert.Throws(() => client.GetAllPublic("org", null)); + + Assert.Throws(() => client.GetAllPublic("")); + Assert.Throws(() => client.GetAllPublic("", ApiOptions.None)); } } diff --git a/Octokit/Clients/IOrganizationMembersClient.cs b/Octokit/Clients/IOrganizationMembersClient.cs index 61c7144b..c72f5bba 100644 --- a/Octokit/Clients/IOrganizationMembersClient.cs +++ b/Octokit/Clients/IOrganizationMembersClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; namespace Octokit @@ -34,6 +33,29 @@ namespace Octokit /// The users Task> GetAll(string org); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// Options for changing the API response + /// The users + Task> GetAll(string org, ApiOptions options); + /// /// /// List all users who are members of an organization. A member is a user that @@ -57,6 +79,30 @@ namespace Octokit /// The users Task> GetAll(string org, OrganizationMembersFilter filter); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The filter to use when getting the users, + /// Options for changing the API response + /// The users + Task> GetAll(string org, OrganizationMembersFilter filter, ApiOptions options); + /// /// /// List all users who are members of an organization. A member is a user that @@ -80,6 +126,30 @@ namespace Octokit /// The users Task> GetAll(string org, OrganizationMembersRole role); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The role filter to use when getting the users, + /// Options for changing the API response + /// The users + Task> GetAll(string org, OrganizationMembersRole role, ApiOptions options); + /// /// /// List all users who are members of an organization. A member is a user that @@ -104,6 +174,31 @@ namespace Octokit /// The users Task> GetAll(string org, OrganizationMembersFilter filter, OrganizationMembersRole role); + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The filter to use when getting the users, + /// The role filter to use when getting the users, + /// Options for changing the API response + /// The users + Task> GetAll(string org, OrganizationMembersFilter filter, OrganizationMembersRole role, ApiOptions options); + /// /// List all users who have publicized their membership of the organization. /// @@ -112,6 +207,15 @@ namespace Octokit /// Task> GetAllPublic(string org); + /// + /// List all users who have publicized their membership of the organization. + /// + /// http://developer.github.com/v3/orgs/members/#public-members-list + /// The login for the organization + /// Options for changing the API response + /// + Task> GetAllPublic(string org, ApiOptions options); + /// /// Check if a user is, publicly or privately, a member of the organization. /// diff --git a/Octokit/Clients/OrganizationMembersClient.cs b/Octokit/Clients/OrganizationMembersClient.cs index ed6cadc5..5462c0a9 100644 --- a/Octokit/Clients/OrganizationMembersClient.cs +++ b/Octokit/Clients/OrganizationMembersClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using Octokit.Internal; @@ -78,7 +77,36 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return ApiConnection.GetAll(ApiUrls.Members(org)); + return GetAll(org, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// Options for changing the API response + /// The users + public Task> GetAll(string org, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Members(org), options); } /// @@ -106,7 +134,37 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return ApiConnection.GetAll(ApiUrls.Members(org, filter)); + return GetAll(org, filter, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The filter to use when getting the users, + /// Options for changing the API response + /// The users + public Task> GetAll(string org, OrganizationMembersFilter filter, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Members(org, filter), options); } /// @@ -134,7 +192,37 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return ApiConnection.GetAll(ApiUrls.Members(org, role)); + return GetAll(org, role, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The role filter to use when getting the users, + /// Options for changing the API response + /// The users + public Task> GetAll(string org, OrganizationMembersRole role, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Members(org, role), options); } /// @@ -163,7 +251,38 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return ApiConnection.GetAll(ApiUrls.Members(org, filter, role)); + return GetAll(org, filter, role, ApiOptions.None); + } + + /// + /// + /// List all users who are members of an organization. A member is a user that + /// belongs to at least 1 team in the organization. + /// + /// + /// If the authenticated user is also an owner of this organization then both + /// concealed and public member will be returned. + /// + /// + /// If the requester is not an owner of the organization the query will be redirected + /// to the public members list. + /// + /// + /// + /// See the API documentation + /// for more information. + /// + /// The login for the organization + /// The filter to use when getting the users, + /// The role filter to use when getting the users, + /// Options for changing the API response + /// The users + public Task> GetAll(string org, OrganizationMembersFilter filter, OrganizationMembersRole role, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Members(org, filter, role), options); } /// @@ -176,7 +295,22 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return ApiConnection.GetAll(ApiUrls.PublicMembers(org)); + return GetAllPublic(org, ApiOptions.None); + } + + /// + /// List all users who have publicized their membership of the organization. + /// + /// http://developer.github.com/v3/orgs/members/#public-members-list + /// The login for the organization + /// Options for changing the API response + /// + public Task> GetAllPublic(string org, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.PublicMembers(org), options); } /// From 029cefe76ab7b028442f89cf66a9f468954a99c4 Mon Sep 17 00:00:00 2001 From: Alexander Efremov Date: Thu, 2 Jun 2016 16:19:39 +0700 Subject: [PATCH 3/6] Add ApiOptions overloads to methods on I(Observable)NotificationsClient (#1330) --- .../Clients/IObservableNotificationsClient.cs | 50 ++- .../Clients/ObservableNotificationsClient.cs | 105 ++++- .../Clients/NotificationsClientTests.cs | 190 ++++++++- Octokit.Tests/Octokit.Tests.csproj | 1 + .../ObservableNotificationsClientTests.cs | 360 ++++++++++++++++++ Octokit/Clients/INotificationsClient.cs | 50 ++- Octokit/Clients/NotificationsClient.cs | 87 ++++- 7 files changed, 819 insertions(+), 24 deletions(-) create mode 100644 Octokit.Tests/Reactive/ObservableNotificationsClientTests.cs diff --git a/Octokit.Reactive/Clients/IObservableNotificationsClient.cs b/Octokit.Reactive/Clients/IObservableNotificationsClient.cs index aa2df53e..709f902e 100644 --- a/Octokit.Reactive/Clients/IObservableNotificationsClient.cs +++ b/Octokit.Reactive/Clients/IObservableNotificationsClient.cs @@ -14,28 +14,74 @@ namespace Octokit.Reactive [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] IObservable GetAllForCurrent(); + /// + /// Retrieves all of the s for the current user. + /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + IObservable GetAllForCurrent(ApiOptions options); + /// /// Retrieves all of the s for the current user specific to the specified repository. /// + /// The owner of the repository. + /// The name of the repository. /// Thrown if the client is not authenticated. /// A of . IObservable GetAllForRepository(string owner, string name); + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + IObservable GetAllForRepository(string owner, string name, ApiOptions options); + /// /// Retrieves all of the s for the current user. /// + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] IObservable GetAllForCurrent(NotificationsRequest request); + /// + /// Retrieves all of the s for the current user. + /// + /// Specifies the parameters to filter notifications by + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + IObservable GetAllForCurrent(NotificationsRequest request, ApiOptions options); + /// /// Retrieves all of the s for the current user specific to the specified repository. /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . IObservable GetAllForRepository(string owner, string name, NotificationsRequest request); + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + IObservable GetAllForRepository(string owner, string name, NotificationsRequest request, ApiOptions options); + /// /// Marks all notifications as read. /// @@ -65,10 +111,10 @@ namespace Octokit.Reactive /// /// The owner of the repository /// The name of the repository - /// The parameter which specifies which notifications to mark. + /// The parameter which specifies which notifications to mark. /// http://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository /// - IObservable MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsRead); + IObservable MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsReadRequest); /// /// Retrives a single by Id. diff --git a/Octokit.Reactive/Clients/ObservableNotificationsClient.cs b/Octokit.Reactive/Clients/ObservableNotificationsClient.cs index 1ba1bf72..775c636e 100644 --- a/Octokit.Reactive/Clients/ObservableNotificationsClient.cs +++ b/Octokit.Reactive/Clients/ObservableNotificationsClient.cs @@ -25,35 +25,88 @@ namespace Octokit.Reactive /// A of . public IObservable GetAllForCurrent() { - return _connection.GetAndFlattenAllPages(ApiUrls.Notifications()); + return GetAllForCurrent(ApiOptions.None); } - /// /// Retrieves all of the s for the current user. /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + public IObservable GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Notifications(), options); + } + + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Thrown if the client is not authenticated. + /// A of . + public IObservable GetAllForRepository(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return GetAllForRepository(owner, name, ApiOptions.None); + } + + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + public IObservable GetAllForRepository(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Notifications(owner, name), options); + } + + /// + /// Retrieves all of the s for the current user. + /// + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . public IObservable GetAllForCurrent(NotificationsRequest request) { Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.Notifications(), request.ToParametersDictionary()); + return GetAllForCurrent(request, ApiOptions.None); } /// - /// Retrieves all of the s for the current user specific to the specified repository. + /// Retrieves all of the s for the current user. /// + /// Specifies the parameters to filter notifications by + /// Options for changing the API response /// Thrown if the client is not authenticated. - /// A of . - public IObservable GetAllForRepository(string owner, string name) + /// A of . + public IObservable GetAllForCurrent(NotificationsRequest request, ApiOptions options) { - return _connection.GetAndFlattenAllPages(ApiUrls.Notifications(owner, name)); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Notifications(), request.ToParametersDictionary(), options); } /// /// Retrieves all of the s for the current user specific to the specified repository. /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . public IObservable GetAllForRepository(string owner, string name, NotificationsRequest request) @@ -62,7 +115,26 @@ namespace Octokit.Reactive Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.Notifications(owner, name), request.ToParametersDictionary()); + return GetAllForRepository(owner, name, request, ApiOptions.None); + } + + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + public IObservable GetAllForRepository(string owner, string name, NotificationsRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Notifications(owner, name), request.ToParametersDictionary(), options); } /// @@ -83,6 +155,8 @@ namespace Octokit.Reactive /// public IObservable MarkAsRead(MarkAsReadRequest markAsReadRequest) { + Ensure.ArgumentNotNull(markAsReadRequest, "markAsReadRequest"); + return _notificationsClient.MarkAsRead(markAsReadRequest).ToObservable(); } @@ -95,6 +169,9 @@ namespace Octokit.Reactive /// public IObservable MarkAsReadForRepository(string owner, string name) { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + return _notificationsClient.MarkAsReadForRepository(owner, name).ToObservable(); } @@ -103,12 +180,16 @@ namespace Octokit.Reactive /// /// The owner of the repository /// The name of the repository - /// The parameter which specifies which notifications to mark. + /// The parameter which specifies which notifications to mark. /// http://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository /// - public IObservable MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsRead) + public IObservable MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsReadRequest) { - return _notificationsClient.MarkAsReadForRepository(owner, name, markAsRead).ToObservable(); + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(markAsReadRequest, "markAsReadRequest"); + + return _notificationsClient.MarkAsReadForRepository(owner, name, markAsReadRequest).ToObservable(); } /// @@ -153,6 +234,8 @@ namespace Octokit.Reactive /// public IObservable SetThreadSubscription(int id, NewThreadSubscription threadSubscription) { + Ensure.ArgumentNotNull(threadSubscription, "threadSubscription"); + return _notificationsClient.SetThreadSubscription(id, threadSubscription).ToObservable(); } diff --git a/Octokit.Tests/Clients/NotificationsClientTests.cs b/Octokit.Tests/Clients/NotificationsClientTests.cs index 97a8dd13..43c8e10d 100644 --- a/Octokit.Tests/Clients/NotificationsClientTests.cs +++ b/Octokit.Tests/Clients/NotificationsClientTests.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Threading.Tasks; using NSubstitute; using Xunit; @@ -27,7 +29,72 @@ namespace Octokit.Tests.Clients client.GetAllForCurrent(); - connection.Received().GetAll(endpoint); + connection.Received().GetAll(endpoint, Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlApiOptions() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var client = new NotificationsClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForCurrent(options); + + connection.Received().GetAll(endpoint, options); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequest() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var client = new NotificationsClient(connection); + + var notificationsRequest = new NotificationsRequest { All = true }; + + client.GetAllForCurrent(notificationsRequest); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 + && d["all"] == "true" && d["participating"] == "false"), Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequestWithApiOptions() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var client = new NotificationsClient(connection); + + var notificationsRequest = new NotificationsRequest { All = true }; + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForCurrent(notificationsRequest, options); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 + && d["all"] == "true" && d["participating"] == "false"), options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new NotificationsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForCurrent((ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForCurrent((NotificationsRequest)null)); } } @@ -42,7 +109,87 @@ namespace Octokit.Tests.Clients client.GetAllForRepository("banana", "split"); - connection.Received().GetAll(endpoint); + connection.Received().GetAll(endpoint, Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var client = new NotificationsClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForRepository("banana", "split", options); + + connection.Received().GetAll(endpoint, options); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequest() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var client = new NotificationsClient(connection); + + var notificationsRequest = new NotificationsRequest { All = true }; + + client.GetAllForRepository("banana", "split", notificationsRequest); + + connection.Received().GetAll(endpoint, Arg.Is>( + d => d.Count == 2 && d["all"] == "true" && d["participating"] == "false"), + Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequestWithApiOptions() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var client = new NotificationsClient(connection); + + var notificationsRequest = new NotificationsRequest { All = true }; + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForRepository("banana", "split", notificationsRequest, options); + + connection.Received().GetAll(endpoint, Arg.Is>( + d => d.Count == 2 && d["all"] == "true" && d["participating"] == "false"), + options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new NotificationsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name")); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null)); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", (ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", new NotificationsRequest())); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, new NotificationsRequest())); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", (NotificationsRequest)null)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name")); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "")); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", new NotificationsRequest())); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", new NotificationsRequest())); } } @@ -74,6 +221,37 @@ namespace Octokit.Tests.Clients connection.Received().Put(endpoint); } + + [Fact] + public void RequestsCorrectUrlParameterized() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var client = new NotificationsClient(connection); + + var markAsReadRequest = new MarkAsReadRequest(); + + client.MarkAsReadForRepository("banana", "split", markAsReadRequest); + + connection.Received().Put(endpoint, markAsReadRequest); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new NotificationsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository(null, "name")); + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository("owner", null)); + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository(null, "name", new MarkAsReadRequest())); + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository("owner", null, new MarkAsReadRequest())); + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository("owner", "name", null)); + + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository("", "name")); + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository("owner", "")); + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository("", "name", new MarkAsReadRequest())); + await Assert.ThrowsAsync(() => client.MarkAsReadForRepository("owner", "", new MarkAsReadRequest())); + } } public class TheGetNotification @@ -135,6 +313,14 @@ namespace Octokit.Tests.Clients connection.Received().Put(endpoint, data); } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new NotificationsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.SetThreadSubscription(1, null)); + } } public class TheDeleteThreadSubscription diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 5e78e372..34824301 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -208,6 +208,7 @@ + diff --git a/Octokit.Tests/Reactive/ObservableNotificationsClientTests.cs b/Octokit.Tests/Reactive/ObservableNotificationsClientTests.cs new file mode 100644 index 00000000..7c83d83a --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableNotificationsClientTests.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableNotificationsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservableNotificationsClient(null)); + } + } + + public class TheGetAllForCurrentMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.GetAllForCurrent(); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, null); + } + + [Fact] + public void RequestsCorrectUrlApiOptions() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForCurrent(options); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2), null); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequest() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + var notificationsRequest = new NotificationsRequest { All = true }; + + client.GetAllForCurrent(notificationsRequest); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2 + && d["all"] == "true" && d["participating"] == "false"), null); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequestWithApiOptions() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + var notificationsRequest = new NotificationsRequest { All = true }; + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForCurrent(notificationsRequest, options); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 4 + && d["all"] == "true" && d["participating"] == "false" + && d["page"] == "1" && d["per_page"] == "1"), null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableNotificationsClient(Substitute.For()); + + Assert.Throws(() => client.GetAllForCurrent((ApiOptions)null)); + Assert.Throws(() => client.GetAllForCurrent((NotificationsRequest)null)); + } + } + + public class TheGetAllForRepository + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.GetAllForRepository("banana", "split"); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, null); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForRepository("banana", "split", options); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2), null); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequest() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + var notificationsRequest = new NotificationsRequest { All = true }; + + client.GetAllForRepository("banana", "split", notificationsRequest); + + connection.Received().Get>(endpoint, Arg.Is>( + d => d.Count == 2 && d["all"] == "true" && d["participating"] == "false"), + null); + } + + [Fact] + public void RequestsCorrectUrlNotificationRequestWithApiOptions() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + var notificationsRequest = new NotificationsRequest { All = true }; + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForRepository("banana", "split", notificationsRequest, options); + + connection.Received().Get>(endpoint, Arg.Is>( + d => d.Count == 4 && d["all"] == "true" && d["participating"] == "false" + && d["page"] == "1" && d["per_page"] == "1"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableNotificationsClient(Substitute.For()); + + Assert.Throws(() => client.GetAllForRepository(null, "name")); + Assert.Throws(() => client.GetAllForRepository("owner", null)); + Assert.Throws(() => client.GetAllForRepository(null, "name", ApiOptions.None)); + Assert.Throws(() => client.GetAllForRepository("owner", null, ApiOptions.None)); + Assert.Throws(() => client.GetAllForRepository("owner", "name", (ApiOptions)null)); + Assert.Throws(() => client.GetAllForRepository(null, "name", new NotificationsRequest())); + Assert.Throws(() => client.GetAllForRepository("owner", null, new NotificationsRequest())); + Assert.Throws(() => client.GetAllForRepository("owner", "name", (NotificationsRequest)null)); + + Assert.Throws(() => client.GetAllForRepository("", "name")); + Assert.Throws(() => client.GetAllForRepository("owner", "")); + Assert.Throws(() => client.GetAllForRepository("", "name", ApiOptions.None)); + Assert.Throws(() => client.GetAllForRepository("owner", "", ApiOptions.None)); + Assert.Throws(() => client.GetAllForRepository("", "name", new NotificationsRequest())); + Assert.Throws(() => client.GetAllForRepository("owner", "", new NotificationsRequest())); + } + } + + public class TheMarkAsRead + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.MarkAsRead(); + + connection.Received().Put(endpoint); + } + } + + public class TheMarkAsReadForRepository + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.MarkAsReadForRepository("banana", "split"); + + connection.Received().Put(endpoint); + } + + [Fact] + public void RequestsCorrectUrlParameterized() + { + var endpoint = new Uri("repos/banana/split/notifications", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + var markAsReadRequest = new MarkAsReadRequest(); + + client.MarkAsReadForRepository("banana", "split", markAsReadRequest); + + connection.Received().Put(endpoint, markAsReadRequest); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableNotificationsClient(Substitute.For()); + + Assert.Throws(() => client.MarkAsReadForRepository(null, "name")); + Assert.Throws(() => client.MarkAsReadForRepository("owner", null)); + Assert.Throws(() => client.MarkAsReadForRepository(null, "name", new MarkAsReadRequest())); + Assert.Throws(() => client.MarkAsReadForRepository("owner", null, new MarkAsReadRequest())); + Assert.Throws(() => client.MarkAsReadForRepository("owner", "name", null)); + + Assert.Throws(() => client.MarkAsReadForRepository("", "name")); + Assert.Throws(() => client.MarkAsReadForRepository("owner", "")); + Assert.Throws(() => client.MarkAsReadForRepository("", "name", new MarkAsReadRequest())); + Assert.Throws(() => client.MarkAsReadForRepository("owner", "", new MarkAsReadRequest())); + } + } + + public class TheGetNotification + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("notifications/threads/1", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.Get(1); + + connection.Received().Get(endpoint, null, null); + } + } + + public class TheMarkNotificationAsRead + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("notifications/threads/1", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.MarkAsRead(1); + + connection.Received().Patch(endpoint); + } + } + + public class TheGetThreadSubscription + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("notifications/threads/1/subscription", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.GetThreadSubscription(1); + + connection.Received().Get(endpoint, null, null); + } + } + + public class TheSetThreadSubscription + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("notifications/threads/1/subscription", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + var data = new NewThreadSubscription(); + + client.SetThreadSubscription(1, data); + + connection.Received().Put(endpoint, data); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableNotificationsClient(Substitute.For()); + + Assert.Throws(() => client.SetThreadSubscription(1, null)); + } + } + + public class TheDeleteThreadSubscription + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("notifications/threads/1/subscription", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableNotificationsClient(gitHubClient); + + client.DeleteThreadSubscription(1); + + connection.Received().Delete(endpoint); + } + } + } +} diff --git a/Octokit/Clients/INotificationsClient.cs b/Octokit/Clients/INotificationsClient.cs index eb92c15a..c40b55a1 100644 --- a/Octokit/Clients/INotificationsClient.cs +++ b/Octokit/Clients/INotificationsClient.cs @@ -23,14 +23,36 @@ namespace Octokit /// /// Retrieves all of the s for the current user. /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + Task> GetAllForCurrent(ApiOptions options); + + /// + /// Retrieves all of the s for the current user. + /// + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] Task> GetAllForCurrent(NotificationsRequest request); + /// + /// Retrieves all of the s for the current user. + /// + /// Specifies the parameters to filter notifications by + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + Task> GetAllForCurrent(NotificationsRequest request, ApiOptions options); + /// /// Retrieves all of the s for the current user specific to the specified repository. /// + /// The owner of the repository. + /// The name of the repository. /// Thrown if the client is not authenticated. /// A of . Task> GetAllForRepository(string owner, string name); @@ -38,10 +60,34 @@ namespace Octokit /// /// Retrieves all of the s for the current user specific to the specified repository. /// + /// The owner of the repository. + /// The name of the repository. + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + Task> GetAllForRepository(string owner, string name, ApiOptions options); + + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . Task> GetAllForRepository(string owner, string name, NotificationsRequest request); + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + Task> GetAllForRepository(string owner, string name, NotificationsRequest request, ApiOptions options); + /// /// Marks all notifications as read. /// @@ -71,10 +117,10 @@ namespace Octokit /// /// The owner of the repository /// The name of the repository - /// The parameter which specifies which notifications to mark. + /// The parameter which specifies which notifications to mark. /// http://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository /// - Task MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsRead); + Task MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsReadRequest); /// /// Retrives a single by Id. diff --git a/Octokit/Clients/NotificationsClient.cs b/Octokit/Clients/NotificationsClient.cs index 017e1311..b0c53c1e 100644 --- a/Octokit/Clients/NotificationsClient.cs +++ b/Octokit/Clients/NotificationsClient.cs @@ -26,24 +26,55 @@ namespace Octokit /// A of . public Task> GetAllForCurrent() { - return ApiConnection.GetAll(ApiUrls.Notifications()); + return GetAllForCurrent(ApiOptions.None); } /// /// Retrieves all of the s for the current user. /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + public Task> GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Notifications(), options); + } + + /// + /// Retrieves all of the s for the current user. + /// + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . public Task> GetAllForCurrent(NotificationsRequest request) { Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.Notifications(), request.ToParametersDictionary()); + return GetAllForCurrent(request, ApiOptions.None); + } + + /// + /// Retrieves all of the s for the current user. + /// + /// Specifies the parameters to filter notifications by + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + public Task> GetAllForCurrent(NotificationsRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Notifications(), request.ToParametersDictionary(), options); } /// /// Retrieves all of the s for the current user specific to the specified repository. /// + /// The owner of the repository. + /// The name of the repository. /// Thrown if the client is not authenticated. /// A of . public Task> GetAllForRepository(string owner, string name) @@ -51,12 +82,32 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return ApiConnection.GetAll(ApiUrls.Notifications(owner, name)); + return GetAllForRepository(owner, name, ApiOptions.None); } /// /// Retrieves all of the s for the current user specific to the specified repository. /// + /// The owner of the repository. + /// The name of the repository. + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + public Task> GetAllForRepository(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Notifications(owner, name), options); + } + + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by /// Thrown if the client is not authenticated. /// A of . public Task> GetAllForRepository(string owner, string name, NotificationsRequest request) @@ -65,7 +116,26 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.Notifications(owner, name), request.ToParametersDictionary()); + return GetAllForRepository(owner, name, request, ApiOptions.None); + } + + /// + /// Retrieves all of the s for the current user specific to the specified repository. + /// + /// The owner of the repository. + /// The name of the repository. + /// Specifies the parameters to filter notifications by + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of . + public Task> GetAllForRepository(string owner, string name, NotificationsRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Notifications(owner, name), request.ToParametersDictionary(), options); } /// @@ -86,6 +156,8 @@ namespace Octokit /// public Task MarkAsRead(MarkAsReadRequest markAsReadRequest) { + Ensure.ArgumentNotNull(markAsReadRequest, "markAsReadRequest"); + return ApiConnection.Put(ApiUrls.Notifications(), markAsReadRequest); } @@ -109,15 +181,16 @@ namespace Octokit /// /// The owner of the repository /// The name of the repository - /// The parameter which specifies which notifications to mark. + /// The parameter which specifies which notifications to mark. /// http://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository /// - public Task MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsRead) + public Task MarkAsReadForRepository(string owner, string name, MarkAsReadRequest markAsReadRequest) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(markAsReadRequest, "markAsRead"); - return ApiConnection.Put(ApiUrls.Notifications(owner, name), markAsRead); + return ApiConnection.Put(ApiUrls.Notifications(owner, name), markAsReadRequest); } /// From dfebfe31b6ef4b5a05545beee94bbeb00a1d0153 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Thu, 2 Jun 2016 11:21:30 +0200 Subject: [PATCH 4/6] Add ApiOption overloads to IIssuesClient and IObservableIssuesClient (#1274) --- .../Clients/IObservableIssuesClient.cs | 139 +++++++++-- .../Clients/ObservableIssuesClient.cs | 226 +++++++++++++++--- .../Clients/IssuesClientTests.cs | 84 +++++-- .../Reactive/ObservableIssuesClientTests.cs | 42 +++- Octokit.Tests/Clients/IssuesClientTests.cs | 147 ++++++++++-- .../Reactive/ObservableIssuesClientTests.cs | 108 ++++++++- Octokit/Clients/IIssuesClient.cs | 127 +++++++++- Octokit/Clients/IssuesClient.cs | 187 +++++++++++++-- 8 files changed, 913 insertions(+), 147 deletions(-) diff --git a/Octokit.Reactive/Clients/IObservableIssuesClient.cs b/Octokit.Reactive/Clients/IObservableIssuesClient.cs index d9ca53a3..26efd33d 100644 --- a/Octokit.Reactive/Clients/IObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/IObservableIssuesClient.cs @@ -42,7 +42,7 @@ namespace Octokit.Reactive /// The owner of the repository /// The name of the repository /// The issue number - /// + /// A signal containing the requested s. [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Method makes a network request")] IObservable Get(string owner, string name, int number); @@ -55,9 +55,21 @@ namespace Octokit.Reactive /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// A signal containing one or more s. IObservable GetAllForCurrent(); + /// + /// Gets all open issues assigned to the authenticated user across all the authenticated user’s visible + /// repositories including owned repositories, member repositories, and organization repositories. + /// + /// Options for changing the API response + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// A signal containing one or more s. + IObservable GetAllForCurrent(ApiOptions options); + /// /// Gets all issues across all the authenticated user’s visible repositories including owned repositories, /// member repositories, and organization repositories. @@ -66,9 +78,21 @@ namespace Octokit.Reactive /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. IObservable GetAllForCurrent(IssueRequest request); + /// + /// Gets all issues across all the authenticated user’s visible repositories including owned repositories, + /// member repositories, and organization repositories. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + IObservable GetAllForCurrent(IssueRequest request, ApiOptions options); + /// /// Gets all open issues assigned to the authenticated user across owned and member repositories for the /// authenticated user. @@ -77,9 +101,21 @@ namespace Octokit.Reactive /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// A signal containing one or more s. IObservable GetAllForOwnedAndMemberRepositories(); + /// + /// Gets all open issues assigned to the authenticated user across owned and member repositories for the + /// authenticated user. + /// + /// Options for changing the API response + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// A signal containing one or more s. + IObservable GetAllForOwnedAndMemberRepositories(ApiOptions options); + /// /// Gets all issues across owned and member repositories for the authenticated user. /// @@ -87,9 +123,20 @@ namespace Octokit.Reactive /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. IObservable GetAllForOwnedAndMemberRepositories(IssueRequest request); + /// + /// Gets all issues across owned and member repositories for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + IObservable GetAllForOwnedAndMemberRepositories(IssueRequest request, ApiOptions options); + /// /// Gets all open issues assigned to the authenticated user for a given organization for the authenticated user. /// @@ -97,9 +144,20 @@ namespace Octokit.Reactive /// http://developer.github.com/v3/issues/#list-issues /// /// The name of the organization - /// + /// A signal containing one or more s. IObservable GetAllForOrganization(string organization); + /// + /// Gets all open issues assigned to the authenticated user for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Options for changing the API response + /// A signal containing one or more s. + IObservable GetAllForOrganization(string organization, ApiOptions options); + /// /// Gets all issues for a given organization for the authenticated user. /// @@ -108,9 +166,21 @@ namespace Octokit.Reactive /// /// The name of the organization /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. IObservable GetAllForOrganization(string organization, IssueRequest request); + /// + /// Gets all issues for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + IObservable GetAllForOrganization(string organization, IssueRequest request, ApiOptions options); + /// /// Gets all open issues assigned to the authenticated user for the repository. /// @@ -119,9 +189,21 @@ namespace Octokit.Reactive /// /// The owner of the repository /// The name of the repository - /// + /// A signal containing one or more s. IObservable GetAllForRepository(string owner, string name); + /// + /// Gets all open issues assigned to the authenticated user for the repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// A signal containing one or more s. + IObservable GetAllForRepository(string owner, string name, ApiOptions options); + /// /// Gets issues for a repository. /// @@ -131,9 +213,22 @@ namespace Octokit.Reactive /// The owner of the repository /// The name of the repository /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. IObservable GetAllForRepository(string owner, string name, RepositoryIssueRequest request); + /// + /// Gets issues for a repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + IObservable GetAllForRepository(string owner, string name, RepositoryIssueRequest request, ApiOptions options); + /// /// Creates an issue for the specified repository. Any user with pull access to a repository can create an /// issue. @@ -142,7 +237,7 @@ namespace Octokit.Reactive /// The owner of the repository /// The name of the repository /// A instance describing the new issue to create - /// + /// A signal containing the new . IObservable Create(string owner, string name, NewIssue newIssue); /// @@ -155,18 +250,18 @@ namespace Octokit.Reactive /// The issue number /// An instance describing the changes to make to the issue /// - /// + /// A signal containing the updated . IObservable Update(string owner, string name, int number, IssueUpdate issueUpdate); - - /// - /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. - /// - /// https://developer.github.com/v3/issues/#lock-an-issue - /// The owner of the repository - /// The name of the repository - /// The issue number - /// - IObservable Lock(string owner, string name, int number); + + /// + /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. + /// + /// https://developer.github.com/v3/issues/#lock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// A signal indicating completion. + IObservable Lock(string owner, string name, int number); /// /// Unlocks an issue for the specified repository. Issue owners and users with push access can unlock an issue. @@ -175,7 +270,7 @@ namespace Octokit.Reactive /// The owner of the repository /// The name of the repository /// The issue number - /// + /// A signal indicating completion. IObservable Unlock(string owner, string name, int number); } } \ No newline at end of file diff --git a/Octokit.Reactive/Clients/ObservableIssuesClient.cs b/Octokit.Reactive/Clients/ObservableIssuesClient.cs index 007b743d..343c057d 100644 --- a/Octokit.Reactive/Clients/ObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssuesClient.cs @@ -55,7 +55,7 @@ namespace Octokit.Reactive /// The owner of the repository /// The name of the repository /// The issue number - /// + /// A signal containing the requested s. public IObservable Get(string owner, string name, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -72,10 +72,27 @@ namespace Octokit.Reactive /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// A signal containing one or more s. public IObservable GetAllForCurrent() { - return GetAllForCurrent(new IssueRequest()); + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// Gets all open issues assigned to the authenticated user across all the authenticated user’s visible + /// repositories including owned repositories, member repositories, and organization repositories. + /// + /// Options for changing the API response + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// A signal containing one or more s. + public IObservable GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return GetAllForCurrent(new IssueRequest(), options); } /// @@ -86,12 +103,30 @@ namespace Octokit.Reactive /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. public IObservable GetAllForCurrent(IssueRequest request) { Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.Issues(), request.ToParametersDictionary()); + return GetAllForCurrent(request, ApiOptions.None); + } + + /// + /// Gets all issues across all the authenticated user’s visible repositories including owned repositories, + /// member repositories, and organization repositories. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + public IObservable GetAllForCurrent(IssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Issues(), request.ToParametersDictionary(), options); } /// @@ -102,12 +137,29 @@ namespace Octokit.Reactive /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// A signal containing one or more s. public IObservable GetAllForOwnedAndMemberRepositories() { return GetAllForOwnedAndMemberRepositories(new IssueRequest()); } + /// + /// Gets all open issues assigned to the authenticated user across owned and member repositories for the + /// authenticated user. + /// + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Options for changing the API response + /// A signal containing one or more s. + public IObservable GetAllForOwnedAndMemberRepositories(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return GetAllForOwnedAndMemberRepositories(new IssueRequest(), options); + } + /// /// Gets all issues across owned and member repositories for the authenticated user. /// @@ -115,12 +167,29 @@ namespace Octokit.Reactive /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. public IObservable GetAllForOwnedAndMemberRepositories(IssueRequest request) { Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.IssuesForOwnedAndMember(), request.ToParametersDictionary()); + return GetAllForOwnedAndMemberRepositories(request, ApiOptions.None); + } + + /// + /// Gets all issues across owned and member repositories for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + public IObservable GetAllForOwnedAndMemberRepositories(IssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.IssuesForOwnedAndMember(), request.ToParametersDictionary(), options); } /// @@ -130,10 +199,29 @@ namespace Octokit.Reactive /// http://developer.github.com/v3/issues/#list-issues /// /// The name of the organization - /// + /// A signal containing one or more s. public IObservable GetAllForOrganization(string organization) { - return GetAllForOrganization(organization, new IssueRequest()); + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + + return GetAllForOrganization(organization, ApiOptions.None); + } + + /// + /// Gets all open issues assigned to the authenticated user for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Options for changing the API response + /// A signal containing one or more s. + public IObservable GetAllForOrganization(string organization, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(options, "options"); + + return GetAllForOrganization(organization, new IssueRequest(), options); } /// @@ -144,13 +232,32 @@ namespace Octokit.Reactive /// /// The name of the organization /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. public IObservable GetAllForOrganization(string organization, IssueRequest request) { Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.Issues(organization), request.ToParametersDictionary()); + return GetAllForOrganization(organization, request, ApiOptions.None); + } + + /// + /// Gets all issues for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + public IObservable GetAllForOrganization(string organization, IssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Issues(organization), request.ToParametersDictionary(), options); } /// @@ -161,10 +268,32 @@ namespace Octokit.Reactive /// /// The owner of the repository /// The name of the repository - /// + /// A signal containing one or more s. public IObservable GetAllForRepository(string owner, string name) { - return GetAllForRepository(owner, name, new RepositoryIssueRequest()); + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return GetAllForRepository(owner, name, ApiOptions.None); + } + + /// + /// Gets all open issues assigned to the authenticated user for the repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// A signal containing one or more s. + public IObservable GetAllForRepository(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return GetAllForRepository(owner, name, new RepositoryIssueRequest(), options); } /// @@ -176,14 +305,35 @@ namespace Octokit.Reactive /// The owner of the repository /// The name of the repository /// Used to filter and sort the list of issues returned - /// + /// A signal containing one or more s. public IObservable GetAllForRepository(string owner, string name, RepositoryIssueRequest request) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.Issues(owner, name), request.ToParametersDictionary()); + return GetAllForRepository(owner, name, request, ApiOptions.None); + } + + /// + /// Gets issues for a repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// A signal containing one or more s. + public IObservable GetAllForRepository(string owner, string name, RepositoryIssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Issues(owner, name), request.ToParametersDictionary(), options); } /// @@ -194,7 +344,7 @@ namespace Octokit.Reactive /// The owner of the repository /// The name of the repository /// A instance describing the new issue to create - /// + /// A signal containing the new . public IObservable Create(string owner, string name, NewIssue newIssue) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -214,7 +364,7 @@ namespace Octokit.Reactive /// The issue number /// An instance describing the changes to make to the issue /// - /// + /// A signal containing the updated . public IObservable Update(string owner, string name, int number, IssueUpdate issueUpdate) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -223,32 +373,32 @@ namespace Octokit.Reactive return _client.Update(owner, name, number, issueUpdate).ToObservable(); } - - /// - /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. - /// - /// https://developer.github.com/v3/issues/#lock-an-issue - /// The owner of the repository - /// The name of the repository - /// The issue number - /// - public IObservable Lock(string owner, string name, int number) + + /// + /// Locks an issue for the specified repository. Issue owners and users with push access can lock an issue. + /// + /// https://developer.github.com/v3/issues/#lock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// A signal indicating completion. + public IObservable Lock(string owner, string name, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); return _client.Lock(owner, name, number).ToObservable(); } - - /// - /// Unlocks an issue for the specified repository. Issue owners and users with push access can unlock an issue. - /// - /// https://developer.github.com/v3/issues/#unlock-an-issue - /// The owner of the repository - /// The name of the repository - /// The issue number - /// - public IObservable Unlock(string owner, string name, int number) + + /// + /// Unlocks an issue for the specified repository. Issue owners and users with push access can unlock an issue. + /// + /// https://developer.github.com/v3/issues/#unlock-an-issue + /// The owner of the repository + /// The name of the repository + /// The issue number + /// A signal indicating completion. + public IObservable Unlock(string owner, string name, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); diff --git a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs index eb12698c..8d076448 100644 --- a/Octokit.Tests.Integration/Clients/IssuesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/IssuesClientTests.cs @@ -1,13 +1,11 @@ using System; using System.Globalization; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Octokit; using Octokit.Tests.Integration; -using Xunit; using Octokit.Tests.Integration.Helpers; -using System.Collections.Generic; +using Xunit; public class IssuesClientTests : IDisposable { @@ -29,15 +27,64 @@ public class IssuesClientTests : IDisposable const string description = "A new unassigned issue"; var newIssue = new NewIssue(title) { Body = description }; var issue = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue); + + Assert.True(issue.Id > 0); + Assert.False(issue.Locked); + Assert.Equal(title, issue.Title); + Assert.Equal(description, issue.Body); + var retrieved = await _issuesClient.Get(_context.RepositoryOwner, _context.RepositoryName, issue.Number); - Assert.NotNull(retrieved); - Assert.NotEqual(0, issue.Id); - Assert.Equal(false, issue.Locked); + Assert.True(retrieved.Id > 0); + Assert.False(retrieved.Locked); Assert.Equal(title, retrieved.Title); Assert.Equal(description, retrieved.Body); } + [IntegrationTest] + public async Task ReturnsPageOfIssuesForARepository() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var issues = await _issuesClient.GetAllForRepository("libgit2", "libgit2sharp", options); + + Assert.Equal(5, issues.Count); + } + + [IntegrationTest] + public async Task ReturnsPageOfIssuesFromStartForARepository() + { + var first = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var firstPage = await _issuesClient.GetAllForRepository("libgit2", "libgit2sharp", first); + + var second = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _issuesClient.GetAllForRepository("libgit2", "libgit2sharp", second); + + Assert.Equal(5, firstPage.Count); + Assert.Equal(5, secondPage.Count); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + Assert.NotEqual(firstPage[2].Id, secondPage[2].Id); + Assert.NotEqual(firstPage[3].Id, secondPage[3].Id); + Assert.NotEqual(firstPage[4].Id, secondPage[4].Id); + } + [IntegrationTest] public async Task CanCreateRetrieveAndCloseIssue() { @@ -54,9 +101,7 @@ public class IssuesClientTests : IDisposable } finally { - var closed = _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, issue.Number, - new IssueUpdate { State = ItemState.Closed }) - .Result; + var closed = _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, issue.Number, new IssueUpdate { State = ItemState.Closed }).Result; Assert.NotNull(closed); } } @@ -87,9 +132,10 @@ public class IssuesClientTests : IDisposable var newIssue3 = new NewIssue("A test issue3") { Body = "A new unassigned issue" }; var newIssue4 = new NewIssue("A test issue4") { Body = "A new unassigned issue" }; await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue1); - Thread.Sleep(1000); + + await Task.Delay(1000); await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue2); - Thread.Sleep(1000); + await Task.Delay(1000); await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue3); var closed = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue4); await _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, closed.Number, @@ -111,9 +157,9 @@ public class IssuesClientTests : IDisposable var newIssue3 = new NewIssue("A test issue3") { Body = "A new unassigned issue" }; var newIssue4 = new NewIssue("A test issue4") { Body = "A new unassigned issue" }; await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue1); - Thread.Sleep(1000); + await Task.Delay(1000); await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue2); - Thread.Sleep(1000); + await Task.Delay(1000); await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue3); var closed = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue4); await _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, closed.Number, @@ -162,7 +208,7 @@ public class IssuesClientTests : IDisposable Assert.Equal("A milestone issue", issues[0].Title); } - [IntegrationTest(Skip = "This is paging for a long long time")] + [IntegrationTest] public async Task CanRetrieveAllIssues() { var newIssue1 = new NewIssue("A test issue1") { Body = "A new unassigned issue" }; @@ -173,13 +219,13 @@ public class IssuesClientTests : IDisposable var issue2 = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue2); var issue3 = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue3); var issue4 = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue4); - await _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, issue4.Number, - new IssueUpdate { State = ItemState.Closed }); + await _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, issue4.Number, new IssueUpdate { State = ItemState.Closed }); - var retrieved = await _issuesClient.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, - new RepositoryIssueRequest { }); + var request = new RepositoryIssueRequest { State = ItemStateFilter.All }; - Assert.True(retrieved.Count >= 4); + var retrieved = await _issuesClient.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, request); + + Assert.Equal(4, retrieved.Count); Assert.True(retrieved.Any(i => i.Number == issue1.Number)); Assert.True(retrieved.Any(i => i.Number == issue2.Number)); Assert.True(retrieved.Any(i => i.Number == issue3.Number)); diff --git a/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs index 9c6acf98..dc195ee1 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableIssuesClientTests.cs @@ -33,11 +33,47 @@ public class ObservableIssuesClientTests : IDisposable } [IntegrationTest] - public async Task ReturnsAllIssuesForARepository() + public async Task ReturnsPageOfIssuesForARepository() { - var issues = await _client.GetAllForRepository("libgit2", "libgit2sharp").ToList(); + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; - Assert.NotEmpty(issues); + var issues = await _client.GetAllForRepository("libgit2", "libgit2sharp", options).ToList(); + + Assert.Equal(5, issues.Count); + } + + [IntegrationTest] + public async Task ReturnsPageOfIssuesFromStartForARepository() + { + var first = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var firstPage = await _client.GetAllForRepository("libgit2", "libgit2sharp", first).ToList(); + + var second = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _client.GetAllForRepository("libgit2", "libgit2sharp", second).ToList(); + + Assert.Equal(5, firstPage.Count); + Assert.Equal(5, secondPage.Count); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + Assert.NotEqual(firstPage[2].Id, secondPage[2].Id); + Assert.NotEqual(firstPage[3].Id, secondPage[3].Id); + Assert.NotEqual(firstPage[4].Id, secondPage[4].Id); } [IntegrationTest] diff --git a/Octokit.Tests/Clients/IssuesClientTests.cs b/Octokit.Tests/Clients/IssuesClientTests.cs index 9dbe0436..131f6ac5 100644 --- a/Octokit.Tests/Clients/IssuesClientTests.cs +++ b/Octokit.Tests/Clients/IssuesClientTests.cs @@ -35,6 +35,17 @@ namespace Octokit.Tests.Clients public class TheGetAllForCurrentMethod { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new IssuesClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForCurrent((ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForCurrent((IssueRequest)null)); + await Assert.ThrowsAsync(() => client.GetAllForCurrent(null, new ApiOptions())); + await Assert.ThrowsAsync(() => client.GetAllForCurrent(new IssueRequest(), null)); + } + [Fact] public void RequestsCorrectUrl() { @@ -44,7 +55,7 @@ namespace Octokit.Tests.Clients client.GetAllForCurrent(); connection.Received().GetAll(Arg.Is(u => u.ToString() == "issues"), - Arg.Any>()); + Arg.Any>(), Args.ApiOptions); } [Fact] @@ -60,12 +71,24 @@ namespace Octokit.Tests.Clients && d["filter"] == "assigned" && d["sort"] == "created" && d["state"] == "open" - && d["direction"] == "asc")); + && d["direction"] == "asc"), + Args.ApiOptions); } } public class TheGetAllForOwnedAndMemberRepositoriesMethod { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new IssuesClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForOwnedAndMemberRepositories((ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForOwnedAndMemberRepositories((IssueRequest)null)); + await Assert.ThrowsAsync(() => client.GetAllForOwnedAndMemberRepositories(null, new ApiOptions())); + await Assert.ThrowsAsync(() => client.GetAllForOwnedAndMemberRepositories(new IssueRequest(), null)); + } + [Fact] public void RequestsCorrectUrl() { @@ -75,12 +98,109 @@ namespace Octokit.Tests.Clients client.GetAllForOwnedAndMemberRepositories(); connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/issues"), - Arg.Any>()); + Arg.Any>(), + Args.ApiOptions); } } - public class TheGetForRepositoryMethod + public class TheGetAllForOrganizationMethod { + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = new IssuesClient(Substitute.For()); + + var options = new ApiOptions(); + var request = new RepositoryIssueRequest(); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, options)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, request)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, request, options)); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization("")); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", options)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", request)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", request, options)); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (IssueRequest)null)); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", null, options)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", request, null)); + } + + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new IssuesClient(connection); + + client.GetAllForOrganization("fake"); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/fake/issues"), + Arg.Any>(), + Args.ApiOptions); + } + + [Fact] + public void SendsAppropriateParameters() + { + var connection = Substitute.For(); + var client = new IssuesClient(connection); + + client.GetAllForOrganization("fake", new RepositoryIssueRequest + { + SortDirection = SortDirection.Ascending + }); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/fake/issues"), + Arg.Is>(d => d.Count == 4 + && d["state"] == "open" + && d["direction"] == "asc" + && d["sort"] == "created" + && d["filter"] == "assigned"), + Args.ApiOptions); + } + } + + public class TheGetAllForRepositoryMethod + { + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = new IssuesClient(Substitute.For()); + + var options = new ApiOptions(); + var request = new RepositoryIssueRequest(); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name")); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", options)); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", request)); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", request, options)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name")); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", options)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", request)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", request, options)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, options)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, request)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, request, options)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "")); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", options)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", request)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", request, options)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", (ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", (RepositoryIssueRequest)null)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", null, options)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", request, null)); + } + [Fact] public void RequestsCorrectUrl() { @@ -90,7 +210,8 @@ namespace Octokit.Tests.Clients client.GetAllForRepository("fake", "repo"); connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/issues"), - Arg.Any>()); + Arg.Any>(), + Args.ApiOptions); } [Fact] @@ -109,20 +230,8 @@ namespace Octokit.Tests.Clients && d["state"] == "open" && d["direction"] == "asc" && d["sort"] == "created" - && d["filter"] == "assigned")); - } - - [Fact] - public async Task EnsuresArgumentsNotNull() - { - var connection = Substitute.For(); - var client = new IssuesClient(connection); - - await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", new RepositoryIssueRequest())); - await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", new RepositoryIssueRequest())); - await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, new RepositoryIssueRequest())); - await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", new RepositoryIssueRequest())); - await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", null)); + && d["filter"] == "assigned"), + Args.ApiOptions); } } diff --git a/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs b/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs index c063e5d4..0360cac1 100644 --- a/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs @@ -36,8 +36,43 @@ public class ObservableIssuesClientTests } } - public class TheGetForRepositoryMethod + public class TheGetAllForRepositoryMethod { + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = new ObservableIssuesClient(Substitute.For()); + + var options = new ApiOptions(); + var request = new RepositoryIssueRequest(); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name").ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", request).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "name", request, options).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name").ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", request).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "name", request, options).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, request).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null, request, options).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "").ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", request).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "", request, options).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", (ApiOptions)null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", (RepositoryIssueRequest)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", null, options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", request, null).ToTask()); + } + [Fact] public async Task ReturnsEveryPageOfIssues() { @@ -82,9 +117,9 @@ public class ObservableIssuesClientTests && d["sort"] == "created" && d["filter"] == "assigned"), Arg.Any()) .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); - gitHubClient.Connection.Get>(secondPageUrl, null, null) + gitHubClient.Connection.Get>(secondPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); - gitHubClient.Connection.Get>(thirdPageUrl, null, null) + gitHubClient.Connection.Get>(thirdPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); var client = new ObservableIssuesClient(gitHubClient); @@ -99,6 +134,21 @@ public class ObservableIssuesClientTests public class TheGetAllForOwnedAndMemberRepositoriesMethod { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableIssuesClient(Substitute.For()); + + await Assert.ThrowsAsync( + () => client.GetAllForOwnedAndMemberRepositories((ApiOptions)null).ToTask()); + await Assert.ThrowsAsync( + () => client.GetAllForOwnedAndMemberRepositories((IssueRequest)null).ToTask()); + await Assert.ThrowsAsync( + () => client.GetAllForOwnedAndMemberRepositories(null, new ApiOptions()).ToTask()); + await Assert.ThrowsAsync( + () => client.GetAllForOwnedAndMemberRepositories(new IssueRequest(), null).ToTask()); + } + [Fact] public async Task ReturnsEveryPageOfIssues() { @@ -141,11 +191,12 @@ public class ObservableIssuesClientTests && d["direction"] == "desc" && d["state"] == "open" && d["sort"] == "created" - && d["filter"] == "assigned"), Arg.Any()) + && d["filter"] == "assigned"), + Arg.Any()) .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); - gitHubClient.Connection.Get>(secondPageUrl, null, null) + gitHubClient.Connection.Get>(secondPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); - gitHubClient.Connection.Get>(thirdPageUrl, null, null) + gitHubClient.Connection.Get>(thirdPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); var client = new ObservableIssuesClient(gitHubClient); @@ -160,6 +211,31 @@ public class ObservableIssuesClientTests public class TheGetAllForOrganizationMethod { + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = new ObservableIssuesClient(Substitute.For()); + + var options = new ApiOptions(); + var request = new RepositoryIssueRequest(); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, request).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, request, options).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization("").ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", request).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", request, options).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (ApiOptions)null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (IssueRequest)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", null, options).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", request, null).ToTask()); + } + [Fact] public async Task ReturnsEveryPageOfIssues() { @@ -204,10 +280,11 @@ public class ObservableIssuesClientTests && d["sort"] == "created" && d["filter"] == "assigned"), Arg.Any()) .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); - gitHubClient.Connection.Get>(secondPageUrl, null, null) + gitHubClient.Connection.Get>(secondPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); - gitHubClient.Connection.Get>(thirdPageUrl, null, null) + gitHubClient.Connection.Get>(thirdPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + var client = new ObservableIssuesClient(gitHubClient); var results = await client.GetAllForOrganization("test").ToArray(); @@ -221,6 +298,17 @@ public class ObservableIssuesClientTests public class TheGetAllForCurrentMethod { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableIssuesClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForCurrent((ApiOptions)null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForCurrent((IssueRequest)null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForCurrent(null, new ApiOptions()).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForCurrent(new IssueRequest(), null).ToTask()); + } + [Fact] public async Task ReturnsEveryPageOfIssues() { @@ -265,9 +353,9 @@ public class ObservableIssuesClientTests && d["sort"] == "created" && d["filter"] == "assigned"), Arg.Any()) .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); - gitHubClient.Connection.Get>(secondPageUrl, null, null) + gitHubClient.Connection.Get>(secondPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); - gitHubClient.Connection.Get>(thirdPageUrl, null, null) + gitHubClient.Connection.Get>(thirdPageUrl, Arg.Any>(), null) .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); var client = new ObservableIssuesClient(gitHubClient); diff --git a/Octokit/Clients/IIssuesClient.cs b/Octokit/Clients/IIssuesClient.cs index bea894bf..e830086b 100644 --- a/Octokit/Clients/IIssuesClient.cs +++ b/Octokit/Clients/IIssuesClient.cs @@ -48,7 +48,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// The issue number - /// + /// The created representing requesting the issue from the API. [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Method makes a network request")] Task Get(string owner, string name, int number); @@ -61,9 +61,21 @@ namespace Octokit /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// The created representing requesting a list of issue from the API. Task> GetAllForCurrent(); + /// + /// Gets all open issues assigned to the authenticated user across all the authenticated user’s visible + /// repositories including owned repositories, member repositories, and organization repositories. + /// + /// Options for changing the API response + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The created representing requesting a list of issue from the API. + Task> GetAllForCurrent(ApiOptions options); + /// /// Gets all issues across all the authenticated user’s visible repositories including owned repositories, /// member repositories, and organization repositories. @@ -72,9 +84,23 @@ namespace Octokit /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// The created representing requesting a list of issue from the API. + Task> GetAllForCurrent(IssueRequest request); + /// + /// Gets all issues across all the authenticated user’s visible repositories including owned repositories, + /// member repositories, and organization repositories. + /// + /// Options for changing the API response + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// The created representing requesting a list of issue from the API. + + Task> GetAllForCurrent(IssueRequest request, ApiOptions options); + /// /// Gets all open issues assigned to the authenticated user across owned and member repositories for the /// authenticated user. @@ -83,9 +109,23 @@ namespace Octokit /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// The created representing requesting a list of issue from the API. + Task> GetAllForOwnedAndMemberRepositories(); + /// + /// Gets all open issues assigned to the authenticated user across owned and member repositories for the + /// authenticated user. + /// + /// Options for changing the API response + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The created representing requesting a list of issue from the API. + + Task> GetAllForOwnedAndMemberRepositories(ApiOptions options); + /// /// Gets all issues across owned and member repositories for the authenticated user. /// @@ -93,9 +133,22 @@ namespace Octokit /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// The created representing requesting a list of issue from the API. + Task> GetAllForOwnedAndMemberRepositories(IssueRequest request); + /// + /// Gets all issues across owned and member repositories for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + + Task> GetAllForOwnedAndMemberRepositories(IssueRequest request, ApiOptions options); + /// /// Gets all open issues assigned to the authenticated user for a given organization for the authenticated user. /// @@ -103,9 +156,20 @@ namespace Octokit /// http://developer.github.com/v3/issues/#list-issues /// /// The name of the organization - /// + /// The created representing requesting a list of issue from the API. Task> GetAllForOrganization(string organization); + /// + /// Gets all open issues assigned to the authenticated user for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + Task> GetAllForOrganization(string organization, ApiOptions options); + /// /// Gets all issues for a given organization for the authenticated user. /// @@ -114,9 +178,21 @@ namespace Octokit /// /// The name of the organization /// Used to filter and sort the list of issues returned - /// + /// The created representing requesting a list of issue from the API. Task> GetAllForOrganization(string organization, IssueRequest request); + /// + /// Gets all issues for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + Task> GetAllForOrganization(string organization, IssueRequest request, ApiOptions options); + /// /// Gets all open issues assigned to the authenticated user for the repository. /// @@ -125,9 +201,21 @@ namespace Octokit /// /// The owner of the repository /// The name of the repository - /// + /// The created representing requesting a list of issue from the API. Task> GetAllForRepository(string owner, string name); + /// + /// Gets all open issues assigned to the authenticated user for the repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + Task> GetAllForRepository(string owner, string name, ApiOptions options); + /// /// Gets issues for a repository. /// @@ -137,9 +225,22 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// Used to filter and sort the list of issues returned - /// + /// The created representing requesting a list of issue from the API. Task> GetAllForRepository(string owner, string name, RepositoryIssueRequest request); + /// + /// Gets issues for a repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + Task> GetAllForRepository(string owner, string name, RepositoryIssueRequest request, ApiOptions options); + /// /// Creates an issue for the specified repository. Any user with pull access to a repository can create an /// issue. @@ -148,7 +249,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// A instance describing the new issue to create - /// + /// The created representing the new issue from the API. Task Create(string owner, string name, NewIssue newIssue); /// @@ -161,7 +262,7 @@ namespace Octokit /// The issue number /// An instance describing the changes to make to the issue /// - /// + /// The created representing the updated issue from the API. Task Update(string owner, string name, int number, IssueUpdate issueUpdate); /// @@ -171,7 +272,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// The issue number - /// + /// The created representing accessing the API. Task Lock(string owner, string name, int number); /// @@ -181,7 +282,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// The issue number - /// + /// The created representing accessing the API. Task Unlock(string owner, string name, int number); } } diff --git a/Octokit/Clients/IssuesClient.cs b/Octokit/Clients/IssuesClient.cs index 4757c014..690e17eb 100644 --- a/Octokit/Clients/IssuesClient.cs +++ b/Octokit/Clients/IssuesClient.cs @@ -56,7 +56,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// The issue number - /// + /// The created representing requesting the issue from the API. public Task Get(string owner, string name, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -73,10 +73,27 @@ namespace Octokit /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// The created representing requesting a list of issue from the API. public Task> GetAllForCurrent() { - return GetAllForCurrent(new IssueRequest()); + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// Gets all open issues assigned to the authenticated user across all the authenticated user’s visible + /// repositories including owned repositories, member repositories, and organization repositories. + /// + /// Options for changing the API response + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The created representing requesting a list of issue from the API. + public Task> GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return GetAllForCurrent(new IssueRequest(), options); } /// @@ -87,12 +104,30 @@ namespace Octokit /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// The created representing requesting a list of issue from the API. public Task> GetAllForCurrent(IssueRequest request) { Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.Issues(), request.ToParametersDictionary()); + return GetAllForCurrent(request, ApiOptions.None); + } + + /// + /// Gets all issues across all the authenticated user’s visible repositories including owned repositories, + /// member repositories, and organization repositories. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + public Task> GetAllForCurrent(IssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Issues(), request.ToParametersDictionary(), options); } /// @@ -103,10 +138,27 @@ namespace Octokit /// Issues are sorted by the create date descending. /// http://developer.github.com/v3/issues/#list-issues /// - /// + /// The created representing requesting a list of issue from the API. public Task> GetAllForOwnedAndMemberRepositories() { - return GetAllForOwnedAndMemberRepositories(new IssueRequest()); + return GetAllForOwnedAndMemberRepositories(ApiOptions.None); + } + + /// + /// Gets all open issues assigned to the authenticated user across owned and member repositories for the + /// authenticated user. + /// + /// Options for changing the API response + /// + /// Issues are sorted by the create date descending. + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The created representing requesting a list of issue from the API. + public Task> GetAllForOwnedAndMemberRepositories(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return GetAllForOwnedAndMemberRepositories(new IssueRequest(), options); } /// @@ -116,13 +168,29 @@ namespace Octokit /// http://developer.github.com/v3/issues/#list-issues /// /// Used to filter and sort the list of issues returned - /// + /// The created representing requesting a list of issue from the API. public Task> GetAllForOwnedAndMemberRepositories(IssueRequest request) { Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.IssuesForOwnedAndMember(), - request.ToParametersDictionary()); + return GetAllForOwnedAndMemberRepositories(request, ApiOptions.None); + } + + /// + /// Gets all issues across owned and member repositories for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + public Task> GetAllForOwnedAndMemberRepositories(IssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.IssuesForOwnedAndMember(), request.ToParametersDictionary(), options); } /// @@ -132,10 +200,29 @@ namespace Octokit /// http://developer.github.com/v3/issues/#list-issues /// /// The name of the organization - /// + /// The created representing requesting a list of issue from the API. public Task> GetAllForOrganization(string organization) { - return GetAllForOrganization(organization, new IssueRequest()); + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + + return GetAllForOrganization(organization, ApiOptions.None); + } + + /// + /// Gets all open issues assigned to the authenticated user for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + public Task> GetAllForOrganization(string organization, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(options, "options"); + + return GetAllForOrganization(organization, new IssueRequest(), options); } /// @@ -146,13 +233,32 @@ namespace Octokit /// /// The name of the organization /// Used to filter and sort the list of issues returned - /// + /// The created representing requesting a list of issue from the API. public Task> GetAllForOrganization(string organization, IssueRequest request) { Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.Issues(organization), request.ToParametersDictionary()); + return GetAllForOrganization(organization, request, ApiOptions.None); + } + + /// + /// Gets all issues for a given organization for the authenticated user. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues + /// + /// The name of the organization + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + public Task> GetAllForOrganization(string organization, IssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Issues(organization), request.ToParametersDictionary(), options); } /// @@ -163,12 +269,27 @@ namespace Octokit /// /// The owner of the repository /// The name of the repository - /// + /// The created representing requesting a list of issue from the API. public Task> GetAllForRepository(string owner, string name) { return GetAllForRepository(owner, name, new RepositoryIssueRequest()); } + /// + /// Gets all open issues assigned to the authenticated user for the repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + public Task> GetAllForRepository(string owner, string name, ApiOptions options) + { + return GetAllForRepository(owner, name, new RepositoryIssueRequest(), options); + } + /// /// Gets issues for a repository. /// @@ -178,15 +299,35 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// Used to filter and sort the list of issues returned - /// - public Task> GetAllForRepository(string owner, string name, - RepositoryIssueRequest request) + /// The created representing requesting a list of issue from the API. + public Task> GetAllForRepository(string owner, string name, RepositoryIssueRequest request) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.Issues(owner, name), request.ToParametersDictionary()); + return GetAllForRepository(owner, name, request, ApiOptions.None); + } + + /// + /// Gets issues for a repository. + /// + /// + /// http://developer.github.com/v3/issues/#list-issues-for-a-repository + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter and sort the list of issues returned + /// Options for changing the API response + /// The created representing requesting a list of issue from the API. + public Task> GetAllForRepository(string owner, string name, RepositoryIssueRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Issues(owner, name), request.ToParametersDictionary(), options); } /// @@ -197,7 +338,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// A instance describing the new issue to create - /// + /// The created representing the new issue from the API. public Task Create(string owner, string name, NewIssue newIssue) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -216,7 +357,7 @@ namespace Octokit /// The issue number /// An instance describing the changes to make to the issue /// - /// + /// The created representing the updated issue from the API. public Task Update(string owner, string name, int number, IssueUpdate issueUpdate) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -233,7 +374,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// The issue number - /// + /// The created representing accessing the API. public Task Lock(string owner, string name, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -249,7 +390,7 @@ namespace Octokit /// The owner of the repository /// The name of the repository /// The issue number - /// + /// The created representing accessing the API. public Task Unlock(string owner, string name, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); From 85a87dade3fa6ac5f3d81ef66eb1fd6a88a6a228 Mon Sep 17 00:00:00 2001 From: Alexander Efremov Date: Thu, 2 Jun 2016 17:15:35 +0700 Subject: [PATCH 5/6] Add ApiOptions overloads to methods on I(Observable)StarredClient (#1336) --- .../Clients/IObservableStarredClient.cs | 105 +++ .../Clients/ObservableStarredClient.cs | 187 ++++- .../Helpers/ConnectionExtensions.cs | 9 + .../Clients/StarredClientTests.cs | 726 +++++++++++++++++- Octokit.Tests/Clients/StarredClientTests.cs | 383 ++++++++- .../Reactive/ObservableStarredClientTests.cs | 457 ++++++++++- Octokit/Clients/IStarredClient.cs | 109 +++ Octokit/Clients/StarredClient.cs | 191 ++++- 8 files changed, 2101 insertions(+), 66 deletions(-) diff --git a/Octokit.Reactive/Clients/IObservableStarredClient.cs b/Octokit.Reactive/Clients/IObservableStarredClient.cs index a1c5c9f6..8938e244 100644 --- a/Octokit.Reactive/Clients/IObservableStarredClient.cs +++ b/Octokit.Reactive/Clients/IObservableStarredClient.cs @@ -13,6 +13,16 @@ namespace Octokit.Reactive /// A of s starring the passed repository IObservable GetAllStargazers(string owner, string name); + /// + /// Retrieves all of the stargazers for the passed repository + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// A of s starring the passed repository + IObservable GetAllStargazers(string owner, string name, ApiOptions options); + /// /// Retrieves all of the stargazers for the passed repository with star creation timestamps. /// @@ -22,6 +32,16 @@ namespace Octokit.Reactive /// A of s starring the passed repository with star creation timestamps. IObservable GetAllStargazersWithTimestamps(string owner, string name); + /// + /// Retrieves all of the stargazers for the passed repository with star creation timestamps. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of s starring the passed repository with star creation timestamps. + IObservable GetAllStargazersWithTimestamps(string owner, string name, ApiOptions options); + /// /// Retrieves all of the starred (ies) for the current user /// @@ -30,6 +50,15 @@ namespace Octokit.Reactive /// A of (ies) starred by the current user /// IObservable GetAllForCurrent(); + + /// + /// Retrieves all of the starred (ies) for the current user + /// + /// Thrown if the client is not authenticated + /// + /// A of (ies) starred by the current user + /// + IObservable GetAllForCurrent(ApiOptions options); /// /// Retrieves all of the starred (ies) for the current user with star creation timestamps. @@ -39,6 +68,15 @@ namespace Octokit.Reactive /// A of (ies) starred by the current authenticated user with star creation timestamps. /// IObservable GetAllForCurrentWithTimestamps(); + + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current authenticated user with star creation timestamps. + /// + IObservable GetAllForCurrentWithTimestamps(ApiOptions options); /// /// Retrieves all of the starred (ies) for the current user @@ -51,6 +89,18 @@ namespace Octokit.Reactive /// IObservable GetAllForCurrent(StarredRequest request); + /// + /// Retrieves all of the starred (ies) for the current user + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters + /// + IObservable GetAllForCurrent(StarredRequest request, ApiOptions options); + /// /// Retrieves all of the starred (ies) for the current user with star creation timestamps. /// @@ -62,6 +112,18 @@ namespace Octokit.Reactive /// IObservable GetAllForCurrentWithTimestamps(StarredRequest request); + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + IObservable GetAllForCurrentWithTimestamps(StarredRequest request, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user /// @@ -70,6 +132,15 @@ namespace Octokit.Reactive /// A starred by the specified user IObservable GetAllForUser(string user); + /// + /// Retrieves all of the (ies) starred by the specified user + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// A starred by the specified user + IObservable GetAllForUser(string user, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. /// @@ -80,6 +151,17 @@ namespace Octokit.Reactive /// IObservable GetAllForUserWithTimestamps(string user); + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A (ies) starred by the specified user with star creation timestamps. + /// + IObservable GetAllForUserWithTimestamps(string user, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user /// @@ -89,6 +171,16 @@ namespace Octokit.Reactive /// A starred by the specified user IObservable GetAllForUser(string user, StarredRequest request); + /// + /// Retrieves all of the (ies) starred by the specified user + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// A starred by the specified user + IObservable GetAllForUser(string user, StarredRequest request, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. /// @@ -101,6 +193,19 @@ namespace Octokit.Reactive /// IObservable GetAllForUserWithTimestamps(string user, StarredRequest request); + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the specified user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + IObservable GetAllForUserWithTimestamps(string user, StarredRequest request, ApiOptions options); + /// /// Check if a repository is starred by the current authenticated user /// diff --git a/Octokit.Reactive/Clients/ObservableStarredClient.cs b/Octokit.Reactive/Clients/ObservableStarredClient.cs index 9f09031f..58e59940 100644 --- a/Octokit.Reactive/Clients/ObservableStarredClient.cs +++ b/Octokit.Reactive/Clients/ObservableStarredClient.cs @@ -29,7 +29,24 @@ namespace Octokit.Reactive Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return _connection.GetAndFlattenAllPages(ApiUrls.Stargazers(owner, name)); + return GetAllStargazers(owner, name, ApiOptions.None); + } + + /// + /// Retrieves all of the stargazers for the passed repository + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// A of s starring the passed repository + public IObservable GetAllStargazers(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Stargazers(owner, name), options); } /// @@ -44,7 +61,24 @@ namespace Octokit.Reactive Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return _connection.GetAndFlattenAllPages(ApiUrls.Stargazers(owner, name), null, AcceptHeaders.StarCreationTimestamps); + return GetAllStargazersWithTimestamps(owner, name, ApiOptions.None); + } + + /// + /// Retrieves all of the stargazers for the passed repository with star creation timestamps. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of s starring the passed repository with star creation timestamps. + public IObservable GetAllStargazersWithTimestamps(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Stargazers(owner, name), null, AcceptHeaders.StarCreationTimestamps, options); } /// @@ -56,7 +90,21 @@ namespace Octokit.Reactive /// public IObservable GetAllForCurrent() { - return _connection.GetAndFlattenAllPages(ApiUrls.Starred()); + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user + /// + /// Thrown if the client is not authenticated + /// + /// A of (ies) starred by the current user + /// + public IObservable GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Starred(), options); } /// @@ -68,7 +116,21 @@ namespace Octokit.Reactive /// public IObservable GetAllForCurrentWithTimestamps() { - return _connection.GetAndFlattenAllPages(ApiUrls.Starred(), null, AcceptHeaders.StarCreationTimestamps); + return GetAllForCurrentWithTimestamps(ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current authenticated user with star creation timestamps. + /// + public IObservable GetAllForCurrentWithTimestamps(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Starred(), null, AcceptHeaders.StarCreationTimestamps, options); } /// @@ -85,7 +147,25 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.Starred(), request.ToParametersDictionary()); + return GetAllForCurrent(request, ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters + /// + public IObservable GetAllForCurrent(StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Starred(), request.ToParametersDictionary(), options); } /// @@ -102,7 +182,25 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.Starred(), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps); + return GetAllForCurrentWithTimestamps(request, ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + public IObservable GetAllForCurrentWithTimestamps(StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Starred(), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps, options); } /// @@ -115,7 +213,22 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNullOrEmptyString(user, "user"); - return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user)); + return GetAllForUser(user, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// A starred by the specified user + public IObservable GetAllForUser(string user, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user), options); } /// @@ -130,7 +243,24 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNullOrEmptyString(user, "user"); - return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user), null, AcceptHeaders.StarCreationTimestamps); + return GetAllForUserWithTimestamps(user, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A (ies) starred by the specified user with star creation timestamps. + /// + public IObservable GetAllForUserWithTimestamps(string user, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user), null, AcceptHeaders.StarCreationTimestamps, options); } /// @@ -146,7 +276,24 @@ namespace Octokit.Reactive Ensure.ArgumentNotNullOrEmptyString(user, "user"); Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user), request.ToParametersDictionary()); + return GetAllForUser(user, request, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated + /// A starred by the specified user + public IObservable GetAllForUser(string user, StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user), request.ToParametersDictionary(), options); } /// @@ -165,7 +312,27 @@ namespace Octokit.Reactive Ensure.ArgumentNotNullOrEmptyString(user, "user"); Ensure.ArgumentNotNull(request, "request"); - return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps); + return GetAllForUserWithTimestamps(user, request, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the specified user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + public IObservable GetAllForUserWithTimestamps(string user, StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.StarredByUser(user), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps, options); } /// diff --git a/Octokit.Reactive/Helpers/ConnectionExtensions.cs b/Octokit.Reactive/Helpers/ConnectionExtensions.cs index 5f2aec15..c60ee366 100644 --- a/Octokit.Reactive/Helpers/ConnectionExtensions.cs +++ b/Octokit.Reactive/Helpers/ConnectionExtensions.cs @@ -37,6 +37,15 @@ namespace Octokit.Reactive.Internal return GetPages(url, parameters, (pageUrl, pageParams) => connection.Get>(pageUrl, pageParams, accepts).ToObservable()); } + public static IObservable GetAndFlattenAllPages(this IConnection connection, Uri url, IDictionary parameters, string accepts, ApiOptions options) + { + return GetPagesWithOptions(url, parameters, options, (pageUrl, pageParams, o) => + { + var passingParameters = Pagination.Setup(parameters, options); + return connection.Get>(pageUrl, passingParameters, accepts).ToObservable(); + }); + } + static IObservable GetPages(Uri uri, IDictionary parameters, Func, IObservable>>> getPageFunc) { diff --git a/Octokit.Tests.Integration/Clients/StarredClientTests.cs b/Octokit.Tests.Integration/Clients/StarredClientTests.cs index e55880e8..82f8a2f3 100644 --- a/Octokit.Tests.Integration/Clients/StarredClientTests.cs +++ b/Octokit.Tests.Integration/Clients/StarredClientTests.cs @@ -1,35 +1,739 @@ using System; -using Octokit.Tests.Integration.Helpers; using System.Linq; using System.Threading.Tasks; +using Octokit.Tests.Integration.Helpers; using Xunit; namespace Octokit.Tests.Integration.Clients { - public class StarredClientTests + public class StarredClientTests : IDisposable { private readonly IGitHubClient _client; private readonly IStarredClient _fixture; + private readonly RepositoryContext _repositoryContext; public StarredClientTests() { _client = Helper.GetAuthenticatedClient(); _fixture = _client.Activity.Starring; + + var github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo1"); + + _repositoryContext = github.CreateRepositoryContext(new NewRepository(repoName)).Result; + + _fixture.RemoveStarFromRepo(_repositoryContext.RepositoryOwner, _repositoryContext.RepositoryName).Wait(); + _fixture.RemoveStarFromRepo("octokit", "octokit.net").Wait(); + _fixture.StarRepo(_repositoryContext.RepositoryOwner, _repositoryContext.RepositoryName).Wait(); + _fixture.StarRepo("octokit", "octokit.net").Wait(); } [IntegrationTest] - public async Task CanCreateAndRetrieveStarsWithTimestamps() + public async Task CanGetAllForCurrent() { - using (var context = await _client.CreateRepositoryContext("public-repo")) + var repositories = await _fixture.GetAllForCurrent(); + Assert.NotEmpty(repositories); + + var repo = repositories.FirstOrDefault(repository => repository.Owner.Login == _repositoryContext.RepositoryOwner && repository.Name == _repositoryContext.RepositoryName); + Assert.NotNull(repo); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForCurrent() + { + var options = new ApiOptions { - await _fixture.RemoveStarFromRepo(context.RepositoryOwner, context.RepositoryName); - await _fixture.StarRepo(context.RepositoryOwner, context.RepositoryName); - var currentUser = await _client.User.Current(); - var userStars = await _fixture.GetAllStargazersWithTimestamps(context.RepositoryOwner, context.RepositoryName); - var userStar = userStars.SingleOrDefault(x => x.User.Id == currentUser.Id); - Assert.NotNull(userStar); - Assert.True(DateTimeOffset.UtcNow.Subtract(userStar.StarredAt) < TimeSpan.FromMinutes(5)); + PageCount = 1, + PageSize = 1 + }; + + var repositories = await _fixture.GetAllForCurrent(options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForCurrent() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var repositories = await _fixture.GetAllForCurrent(options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForCurrent() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForCurrent(startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForCurrent(skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); + } + + [IntegrationTest] + public async Task CanGetAllForCurrentParameterized() + { + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + + var repositories = await _fixture.GetAllForCurrent(starredRequest); + Assert.NotEmpty(repositories); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForCurrentParameterized() + { + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var repositories = await _fixture.GetAllForCurrent(starredRequest, options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForCurrentParameterized() + { + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var repositories = await _fixture.GetAllForCurrent(starredRequest, options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForCurrentParameterized() + { + var starredRequestFirstPage = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + var starredRequestSecondPage = new StarredRequest { SortDirection = SortDirection.Descending, SortProperty = StarredSort.Updated }; + + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForCurrent(starredRequestFirstPage, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForCurrent(starredRequestSecondPage, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); + } + + [IntegrationTest] + public async Task CanGetAllForCurrentWithTimestamps() + { + var stars = await _fixture.GetAllForCurrentWithTimestamps(); + Assert.NotEmpty(stars); + + var repo = stars.FirstOrDefault(star => star.Repo.Owner.Login == _repositoryContext.RepositoryOwner && star.Repo.Name == _repositoryContext.RepositoryName); + Assert.NotNull(repo); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForCurrentWithTimestamps() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var repositories = await _fixture.GetAllForCurrentWithTimestamps(options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForCurrentWithTimestamps() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var repositories = await _fixture.GetAllForCurrentWithTimestamps(options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForCurrentWithTimestamps() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForCurrentWithTimestamps(startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForCurrentWithTimestamps(skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Repo.Id, secondPage.First().Repo.Id); + } + + [IntegrationTest] + public async Task CanGetAllForCurrentWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortProperty = StarredSort.Created, SortDirection = SortDirection.Descending }; + + var stars = await _fixture.GetAllForCurrentWithTimestamps(starredRequest); + Assert.NotEmpty(stars); + + var repo = stars.FirstOrDefault(star => star.Repo.Owner.Login == _repositoryContext.RepositoryOwner && star.Repo.Name == _repositoryContext.RepositoryName); + Assert.NotNull(repo); + + for (int i = 1; i < stars.Count; i++) + { + Assert.True(stars[i - 1].StarredAt >= stars[i].StarredAt); } } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForCurrentWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortProperty = StarredSort.Created, SortDirection = SortDirection.Descending }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var stars = await _fixture.GetAllForCurrentWithTimestamps(starredRequest, options); + Assert.Equal(1, stars.Count); + + for (int i = 1; i < stars.Count; i++) + { + Assert.True(stars[i - 1].StarredAt >= stars[i].StarredAt); + } + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForCurrentWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortProperty = StarredSort.Created, SortDirection = SortDirection.Descending }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var stars = await _fixture.GetAllForCurrentWithTimestamps(starredRequest, options); + Assert.Equal(1, stars.Count); + + for (int i = 1; i < stars.Count; i++) + { + Assert.True(stars[i - 1].StarredAt >= stars[i].StarredAt); + } + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForCurrentWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortDirection = SortDirection.Descending, SortProperty = StarredSort.Created }; + + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForCurrentWithTimestamps(starredRequest, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForCurrentWithTimestamps(starredRequest, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Repo.Id, secondPage.First().Repo.Id); + + for (int i = 1; i < firstPage.Count; i++) + { + Assert.True(firstPage[i].StarredAt >= secondPage[i].StarredAt); + } + } + + [IntegrationTest] + public async Task CanGetAllForUser() + { + var repositories = await _fixture.GetAllForUser(Helper.UserName); + Assert.NotEmpty(repositories); + + var repo = repositories.FirstOrDefault(repository => repository.Owner.Login == _repositoryContext.RepositoryOwner && repository.Name == _repositoryContext.RepositoryName); + Assert.NotNull(repo); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForUser() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var repositories = await _fixture.GetAllForUser(Helper.UserName, options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForUser() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var repositories = await _fixture.GetAllForUser(Helper.UserName, options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForUser() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForUser(Helper.UserName, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForUser(Helper.UserName, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); + } + + [IntegrationTest] + public async Task CanGetAllForUserParameterized() + { + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + + var repositories = await _fixture.GetAllForUser(Helper.UserName, starredRequest); + Assert.NotEmpty(repositories); + + var repo = repositories.FirstOrDefault(repository => repository.Owner.Login == _repositoryContext.RepositoryOwner && repository.Name == _repositoryContext.RepositoryName); + Assert.NotNull(repo); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForUserParameterized() + { + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var repositories = await _fixture.GetAllForUser(Helper.UserName, starredRequest, options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForUserParameterized() + { + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var repositories = await _fixture.GetAllForUser(Helper.UserName, starredRequest, options); + Assert.Equal(1, repositories.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForUserParameterized() + { + var starredRequestFirstPage = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + var starredRequestSecondPage = new StarredRequest { SortDirection = SortDirection.Ascending, SortProperty = StarredSort.Created }; + + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForUser(Helper.UserName, starredRequestFirstPage, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForUser(Helper.UserName, starredRequestSecondPage, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); + } + + [IntegrationTest] + public async Task CanGetAllForUserWithTimestamps() + { + var stars = await _fixture.GetAllForUserWithTimestamps(Helper.UserName); + Assert.NotEmpty(stars); + + var star = stars.FirstOrDefault(repositoryStar => repositoryStar.Repo.Owner.Login == _repositoryContext.RepositoryOwner && repositoryStar.Repo.Name == _repositoryContext.RepositoryName); + Assert.NotNull(star); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForUserWithTimestamps() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var stars = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, options); + Assert.Equal(1, stars.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForUserWithTimestamps() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var stars = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, options); + Assert.Equal(1, stars.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForUserWithTimestamps() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Repo.Id, secondPage.First().Repo.Id); + } + + [IntegrationTest] + public async Task CanGetAllForUserWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortProperty = StarredSort.Created, SortDirection = SortDirection.Descending }; + + var stars = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, starredRequest); + Assert.NotEmpty(stars); + + var repo = stars.FirstOrDefault(repository => repository.Repo.Owner.Login == _repositoryContext.RepositoryOwner && repository.Repo.Name == _repositoryContext.RepositoryName); + Assert.NotNull(repo); + + for (int i = 1; i < stars.Count; i++) + { + Assert.True(stars[i - 1].StarredAt >= stars[i].StarredAt); + } + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartForUserWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortProperty = StarredSort.Created, SortDirection = SortDirection.Descending }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var stars = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, starredRequest, options); + Assert.Equal(1, stars.Count); + + for (int i = 1; i < stars.Count; i++) + { + Assert.True(stars[i - 1].StarredAt >= stars[i].StarredAt); + } + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartForUserWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortProperty = StarredSort.Created, SortDirection = SortDirection.Ascending }; + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var stars = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, starredRequest, options); + Assert.Equal(1, stars.Count); + + for (int i = 1; i < stars.Count; i++) + { + Assert.True(stars[i - 1].StarredAt >= stars[i].StarredAt); + } + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageForUserWithTimestampsParameterized() + { + var starredRequest = new StarredRequest { SortProperty = StarredSort.Created, SortDirection = SortDirection.Descending }; + + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, starredRequest, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllForUserWithTimestamps(Helper.UserName, starredRequest, skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Repo.Id, secondPage.First().Repo.Id); + + for (int i = 0; i < firstPage.Count; i++) + { + Assert.True(firstPage[i].StarredAt >= secondPage[i].StarredAt); + } + } + + [IntegrationTest] + public async Task CanGetAllStargazers() + { + var users = await _fixture.GetAllStargazers(_repositoryContext.RepositoryOwner, _repositoryContext.RepositoryName); + Assert.NotEmpty(users); + + var user = users.FirstOrDefault(u => u.Login == Helper.UserName); + Assert.NotNull(user); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartAllStargazers() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var users = await _fixture.GetAllStargazers("octokit", "octokit.net", options); + Assert.Equal(1, users.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartAllStargazers() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var users = await _fixture.GetAllStargazers("octokit", "octokit.net", options); + Assert.Equal(1, users.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageAllStargazers() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllStargazers("octokit", "octokit.net", startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllStargazers("octokit", "octokit.net", skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().Id, secondPage.First().Id); + } + + [IntegrationTest] + public async Task CanGetAllStargazersWithTimestamps() + { + var users = await _fixture.GetAllStargazersWithTimestamps(_repositoryContext.RepositoryOwner, _repositoryContext.RepositoryName); + Assert.NotEmpty(users); + + var userStar = users.FirstOrDefault(star => star.User.Login == _repositoryContext.RepositoryOwner); + Assert.NotNull(userStar); + + Assert.True(DateTimeOffset.UtcNow.Subtract(userStar.StarredAt) < TimeSpan.FromMinutes(5)); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithoutStartAllStargazersWithTimestamps() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var userStars = await _fixture.GetAllStargazersWithTimestamps("octokit", "octokit.net", options); + Assert.Equal(1, userStars.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfRepositoriesWithStartAllStargazersWithTimestamps() + { + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var userStars = await _fixture.GetAllStargazersWithTimestamps("octokit", "octokit.net", options); + Assert.Equal(1, userStars.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctRepositoriesBasedOnStartPageAllStargazersWithTimestamps() + { + var startOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPage = await _fixture.GetAllStargazersWithTimestamps("octokit", "octokit.net", startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _fixture.GetAllStargazersWithTimestamps("octokit", "octokit.net", skipStartOptions); + + Assert.Equal(1, firstPage.Count); + Assert.Equal(1, secondPage.Count); + Assert.NotEqual(firstPage.First().StarredAt, secondPage.First().StarredAt); + } + + public void Dispose() + { + _repositoryContext.Dispose(); + } } } diff --git a/Octokit.Tests/Clients/StarredClientTests.cs b/Octokit.Tests/Clients/StarredClientTests.cs index e0543bad..9793dcbd 100644 --- a/Octokit.Tests/Clients/StarredClientTests.cs +++ b/Octokit.Tests/Clients/StarredClientTests.cs @@ -23,30 +23,323 @@ namespace Octokit.Tests.Clients public class TheGetAllForCurrentMethod { [Fact] - public void RequestsCorrectUrl() + public async Task RequestsCorrectUrl() { var endpoint = new Uri("user/starred", UriKind.Relative); var connection = Substitute.For(); var client = new StarredClient(connection); - client.GetAllForCurrent(); + await client.GetAllForCurrent(); - connection.Received().GetAll(endpoint); + connection.Received().GetAll(endpoint, Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + await client.GetAllForCurrent(options); + + connection.Received().GetAll(endpoint, options); + } + + [Fact] + public async Task RequestsCorrectUrlParametrized() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForCurrent(request); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlParametrizedWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForCurrent(request, options); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), options); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestamps() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + await client.GetAllForCurrentWithTimestamps(); + + connection.Received().GetAll(endpoint, null, "application/vnd.github.v3.star+json", Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestampsWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + await client.GetAllForCurrentWithTimestamps(options); + + connection.Received().GetAll(endpoint, null, "application/vnd.github.v3.star+json", options); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestampsParametrized() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForCurrentWithTimestamps(request); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + "application/vnd.github.v3.star+json", Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestampsParametrizedWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForCurrentWithTimestamps(request, options); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + "application/vnd.github.v3.star+json", options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new StarredClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForCurrent((ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForCurrent((StarredRequest)null)); + await Assert.ThrowsAsync(() => client.GetAllForCurrentWithTimestamps((ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForCurrentWithTimestamps((StarredRequest)null)); + + await Assert.ThrowsAsync(() => client.GetAllForCurrent(null, new ApiOptions())); + await Assert.ThrowsAsync(() => client.GetAllForCurrent(new StarredRequest(), null)); + await Assert.ThrowsAsync(() => client.GetAllForCurrentWithTimestamps(null, new ApiOptions())); + await Assert.ThrowsAsync(() => client.GetAllForCurrentWithTimestamps(new StarredRequest(), null)); } } public class TheGetAllForUserMethod { [Fact] - public void RequestsCorrectUrl() + public async Task RequestsCorrectUrl() { var endpoint = new Uri("users/banana/starred", UriKind.Relative); var connection = Substitute.For(); var client = new StarredClient(connection); - client.GetAllForUser("banana"); + await client.GetAllForUser("banana"); - connection.Received().GetAll(endpoint); + connection.Received().GetAll(endpoint, Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + await client.GetAllForUser("banana", options); + + connection.Received().GetAll(endpoint, options); + } + + [Fact] + public async Task RequestsCorrectUrlParametrized() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForUser("banana", starredRequest); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlParametrizedWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForUser("banana", starredRequest, options); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), options); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestamps() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + await client.GetAllForUserWithTimestamps("banana"); + + connection.Received().GetAll(endpoint, null, + "application/vnd.github.v3.star+json", Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestampsWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + await client.GetAllForUserWithTimestamps("banana", options); + + connection.Received().GetAll(endpoint, null, "application/vnd.github.v3.star+json", options); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestampsParametrized() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForUserWithTimestamps("banana", starredRequest); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + "application/vnd.github.v3.star+json", Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithTimestampsParametrizedWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + await client.GetAllForUserWithTimestamps("banana", starredRequest, options); + + connection.Received().GetAll(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + "application/vnd.github.v3.star+json", options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new StarredClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForUser(null)); + await Assert.ThrowsAsync(() => client.GetAllForUser(null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUser("banana", (ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForUser(null, new StarredRequest())); + await Assert.ThrowsAsync(() => client.GetAllForUser("banana", (StarredRequest)null)); + await Assert.ThrowsAsync(() => client.GetAllForUser(null, new StarredRequest(), ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUser("banana", null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUser("banana", new StarredRequest(), null)); + + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps(null)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps(null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps("banana", (ApiOptions)null)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps(null, new StarredRequest())); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps("banana", (StarredRequest)null)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps(null, new StarredRequest(), ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps("banana", null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps("banana", new StarredRequest(), null)); + + await Assert.ThrowsAsync(() => client.GetAllForUser("")); + await Assert.ThrowsAsync(() => client.GetAllForUser("", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUser("", new StarredRequest(), ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps("")); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps("", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForUserWithTimestamps("", new StarredRequest(), ApiOptions.None)); } } @@ -61,7 +354,83 @@ namespace Octokit.Tests.Clients client.GetAllStargazers("fight", "club"); - connection.Received().GetAll(endpoint); + connection.Received().GetAll(endpoint, Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllStargazers("fight", "club", options); + + connection.Received().GetAll(endpoint, options); + } + + [Fact] + public void RequestsCorrectUrlWithTimestamps() + { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + client.GetAllStargazersWithTimestamps("fight", "club"); + + connection.Received().GetAll(endpoint, null, "application/vnd.github.v3.star+json", Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsWithApiOptions() + { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllStargazersWithTimestamps("fight", "club", options); + + connection.Received().GetAll(endpoint, null, "application/vnd.github.v3.star+json", options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new StarredClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllStargazers(null, "club")); + await Assert.ThrowsAsync(() => client.GetAllStargazers("fight", null)); + await Assert.ThrowsAsync(() => client.GetAllStargazers(null, "club", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllStargazers("fight", null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllStargazers("fight", "club", null)); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps(null, "club")); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps("fight", null)); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps(null, "club", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps("fight", null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps("fight", "club", null)); + + await Assert.ThrowsAsync(() => client.GetAllStargazers("", "club")); + await Assert.ThrowsAsync(() => client.GetAllStargazers("fight", "")); + await Assert.ThrowsAsync(() => client.GetAllStargazers("", "club", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllStargazers("fight", "", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps("", "club")); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps("fight", "")); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps("", "club", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllStargazersWithTimestamps("fight", "", ApiOptions.None)); } } diff --git a/Octokit.Tests/Reactive/ObservableStarredClientTests.cs b/Octokit.Tests/Reactive/ObservableStarredClientTests.cs index c6924dd6..8d2843fa 100644 --- a/Octokit.Tests/Reactive/ObservableStarredClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableStarredClientTests.cs @@ -22,62 +22,463 @@ namespace Octokit.Tests.Reactive public class TheGetAllStargazersMethod { [Fact] - public async Task EnsuresArguments() - { - var client = new ObservableStarredClient(Substitute.For()); - - await Assert.ThrowsAsync(() => client.GetAllStargazers(null, "name").ToTask()); - await Assert.ThrowsAsync(() => client.GetAllStargazers("owner", null).ToTask()); - } - - [Fact] - public void GetsStargazersFromClient() + public void RequestsCorrectUrl() { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); var connection = Substitute.For(); var gitHubClient = Substitute.For(); gitHubClient.Connection.Returns(connection); var client = new ObservableStarredClient(gitHubClient); - client.GetAllStargazers("jugglingnutcase", "katiejamie"); - connection.Received().Get>(ApiUrls.Stargazers("jugglingnutcase", "katiejamie"), null, null); + client.GetAllStargazers("fight", "club"); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, null); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllStargazers("fight", "club", options); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2), null); + } + + [Fact] + public void RequestsCorrectUrlWithTimestamps() + { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + client.GetAllStargazersWithTimestamps("fight", "club"); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, "application/vnd.github.v3.star+json"); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsWithApiOptions() + { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllStargazersWithTimestamps("fight", "club", options); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2), "application/vnd.github.v3.star+json"); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableStarredClient(Substitute.For()); + + Assert.Throws(() => client.GetAllStargazers(null, "club")); + Assert.Throws(() => client.GetAllStargazers("fight", null)); + Assert.Throws(() => client.GetAllStargazers(null, "club", ApiOptions.None)); + Assert.Throws(() => client.GetAllStargazers("fight", null, ApiOptions.None)); + Assert.Throws(() => client.GetAllStargazers("fight", "club", null)); + Assert.Throws(() => client.GetAllStargazersWithTimestamps(null, "club")); + Assert.Throws(() => client.GetAllStargazersWithTimestamps("fight", null)); + Assert.Throws(() => client.GetAllStargazersWithTimestamps(null, "club", ApiOptions.None)); + Assert.Throws(() => client.GetAllStargazersWithTimestamps("fight", null, ApiOptions.None)); + Assert.Throws(() => client.GetAllStargazersWithTimestamps("fight", "club", null)); + + Assert.Throws(() => client.GetAllStargazers("", "club")); + Assert.Throws(() => client.GetAllStargazers("fight", "")); + Assert.Throws(() => client.GetAllStargazers("", "club", ApiOptions.None)); + Assert.Throws(() => client.GetAllStargazers("fight", "", ApiOptions.None)); + Assert.Throws(() => client.GetAllStargazersWithTimestamps("", "club")); + Assert.Throws(() => client.GetAllStargazersWithTimestamps("fight", "")); + Assert.Throws(() => client.GetAllStargazersWithTimestamps("", "club", ApiOptions.None)); + Assert.Throws(() => client.GetAllStargazersWithTimestamps("fight", "", ApiOptions.None)); } } public class TheGetAllForCurrentMethod { [Fact] - public void GetsStarsForCurrent() + public void RequestsCorrectUrl() { + var endpoint = new Uri("user/starred", UriKind.Relative); var connection = Substitute.For(); var gitHubClient = Substitute.For(); gitHubClient.Connection.Returns(connection); var client = new ObservableStarredClient(gitHubClient); client.GetAllForCurrent(); - connection.Received().Get>(ApiUrls.Starred(), null, null); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, null); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForCurrent(options); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2 && d["per_page"] == "1" && d["page"] == "1"), null); + } + + [Fact] + public void RequestsCorrectUrlParametrized() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForCurrent(request); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + null); + } + + [Fact] + public void RequestsCorrectUrlParametrizedWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForCurrent(request, options); + + connection.Received().Get>(endpoint, + Arg.Is>(d => d.Count == 4 && d["direction"] == "asc" && d["per_page"] == "1" && d["page"] == "1"), null); + } + + [Fact] + public void RequestsCorrectUrlWithTimestamps() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + client.GetAllForCurrentWithTimestamps(); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, "application/vnd.github.v3.star+json"); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForCurrentWithTimestamps(options); + + connection.Received().Get>(endpoint, + Arg.Is>(d => d.Count == 2 && d["per_page"] == "1" && d["page"] == "1"), + "application/vnd.github.v3.star+json"); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsParametrized() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForCurrentWithTimestamps(request); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + "application/vnd.github.v3.star+json"); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsParametrizedWithApiOptions() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var request = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForCurrentWithTimestamps(request, options); + + connection.Received().Get>(endpoint, + Arg.Is>(d => d.Count == 4 && d["direction"] == "asc" && d["per_page"] == "1" && d["page"] == "1"), + "application/vnd.github.v3.star+json"); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableStarredClient(Substitute.For()); + + Assert.Throws(() => client.GetAllForCurrent((ApiOptions)null)); + Assert.Throws(() => client.GetAllForCurrent((StarredRequest)null)); + Assert.Throws(() => client.GetAllForCurrentWithTimestamps((ApiOptions)null)); + Assert.Throws(() => client.GetAllForCurrentWithTimestamps((StarredRequest)null)); + + Assert.Throws(() => client.GetAllForCurrent(null, new ApiOptions())); + Assert.Throws(() => client.GetAllForCurrent(new StarredRequest(), null)); + Assert.Throws(() => client.GetAllForCurrentWithTimestamps(null, new ApiOptions())); + Assert.Throws(() => client.GetAllForCurrentWithTimestamps(new StarredRequest(), null)); } } public class TheGetAllForUserMethod { [Fact] - public async Task EnsuresArguments() - { - var client = new ObservableStarredClient(Substitute.For()); - - await Assert.ThrowsAsync(() => client.GetAllForUser(null).ToTask()); - } - - [Fact] - public void GetsStarsForUser() + public void RequestsCorrectUrl() { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); var connection = Substitute.For(); var gitHubClient = Substitute.For(); gitHubClient.Connection.Returns(connection); var client = new ObservableStarredClient(gitHubClient); - client.GetAllForUser("jugglingnutcase"); - connection.Received().Get>(ApiUrls.StarredByUser("jugglingnutcase"), null, null); + client.GetAllForUser("banana"); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, null); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForUser("banana", options); + + connection.Received().Get>(endpoint, + Arg.Is>(d => d.Count == 2 && d["per_page"] == "1" && d["page"] == "1"), null); + } + + [Fact] + public void RequestsCorrectUrlParametrized() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForUser("banana", starredRequest); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), null); + } + + [Fact] + public void RequestsCorrectUrlParametrizedWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForUser("banana", starredRequest, options); + + connection.Received().Get>(endpoint, + Arg.Is>(d => d.Count == 4 && d["direction"] == "asc" && d["direction"] == "asc" && d["per_page"] == "1" && d["page"] == "1"), + null); + } + + [Fact] + public void RequestsCorrectUrlWithTimestamps() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + client.GetAllForUserWithTimestamps("banana"); + + connection.Received().Get>(endpoint, Args.EmptyDictionary, + "application/vnd.github.v3.star+json"); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + client.GetAllForUserWithTimestamps("banana", options); + + connection.Received().Get>(endpoint, + Arg.Is>(d => d.Count == 2 && d["per_page"] == "1" && d["page"] == "1"), + "application/vnd.github.v3.star+json"); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsParametrized() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForUserWithTimestamps("banana", starredRequest); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 2 && d["direction"] == "asc"), + "application/vnd.github.v3.star+json"); + } + + [Fact] + public void RequestsCorrectUrlWithTimestampsParametrizedWithApiOptions() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var gitHubClient = Substitute.For(); + gitHubClient.Connection.Returns(connection); + var client = new ObservableStarredClient(gitHubClient); + + var options = new ApiOptions + { + PageCount = 1, + StartPage = 1, + PageSize = 1 + }; + + var starredRequest = new StarredRequest { SortDirection = SortDirection.Ascending }; + + client.GetAllForUserWithTimestamps("banana", starredRequest, options); + + connection.Received().Get>(endpoint, Arg.Is>(d => d.Count == 4 && d["direction"] == "asc" && d["direction"] == "asc" && d["per_page"] == "1" && d["page"] == "1"), + "application/vnd.github.v3.star+json"); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableStarredClient(Substitute.For()); + + Assert.Throws(() => client.GetAllForUser(null)); + Assert.Throws(() => client.GetAllForUser(null, ApiOptions.None)); + Assert.Throws(() => client.GetAllForUser("banana", (ApiOptions)null)); + Assert.Throws(() => client.GetAllForUser(null, new StarredRequest())); + Assert.Throws(() => client.GetAllForUser("banana", (StarredRequest)null)); + Assert.Throws(() => client.GetAllForUser(null, new StarredRequest(), ApiOptions.None)); + Assert.Throws(() => client.GetAllForUser("banana", null, ApiOptions.None)); + Assert.Throws(() => client.GetAllForUser("banana", new StarredRequest(), null)); + + Assert.Throws(() => client.GetAllForUserWithTimestamps(null)); + Assert.Throws(() => client.GetAllForUserWithTimestamps(null, ApiOptions.None)); + Assert.Throws(() => client.GetAllForUserWithTimestamps("banana", (ApiOptions)null)); + Assert.Throws(() => client.GetAllForUserWithTimestamps(null, new StarredRequest())); + Assert.Throws(() => client.GetAllForUserWithTimestamps("banana", (StarredRequest)null)); + Assert.Throws(() => client.GetAllForUserWithTimestamps(null, new StarredRequest(), ApiOptions.None)); + Assert.Throws(() => client.GetAllForUserWithTimestamps("banana", null, ApiOptions.None)); + Assert.Throws(() => client.GetAllForUserWithTimestamps("banana", new StarredRequest(), null)); + + Assert.Throws(() => client.GetAllForUser("")); + Assert.Throws(() => client.GetAllForUser("", ApiOptions.None)); + Assert.Throws(() => client.GetAllForUser("", new StarredRequest(), ApiOptions.None)); + Assert.Throws(() => client.GetAllForUserWithTimestamps("")); + Assert.Throws(() => client.GetAllForUserWithTimestamps("", ApiOptions.None)); + Assert.Throws(() => client.GetAllForUserWithTimestamps("", new StarredRequest(), ApiOptions.None)); } } @@ -93,7 +494,7 @@ namespace Octokit.Tests.Reactive } [Fact] - public async Task ChecksStarredForUser() + public void ChecksStarredForUser() { var gitHubClient = Substitute.For(); var client = new ObservableStarredClient(gitHubClient); @@ -115,7 +516,7 @@ namespace Octokit.Tests.Reactive } [Fact] - public async Task ChecksStarredForUser() + public void ChecksStarredForUser() { var gitHubClient = Substitute.For(); var client = new ObservableStarredClient(gitHubClient); @@ -137,7 +538,7 @@ namespace Octokit.Tests.Reactive } [Fact] - public async Task ChecksStarredForUser() + public void ChecksStarredForUser() { var gitHubClient = Substitute.For(); var client = new ObservableStarredClient(gitHubClient); diff --git a/Octokit/Clients/IStarredClient.cs b/Octokit/Clients/IStarredClient.cs index 078f5707..6aeaf127 100644 --- a/Octokit/Clients/IStarredClient.cs +++ b/Octokit/Clients/IStarredClient.cs @@ -20,6 +20,16 @@ namespace Octokit /// A of s starring the passed repository. Task> GetAllStargazers(string owner, string name); + /// + /// Retrieves all of the stargazers for the passed repository. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of s starring the passed repository. + Task> GetAllStargazers(string owner, string name, ApiOptions options); + /// /// Retrieves all of the stargazers for the passed repository with star creation timestamps. /// @@ -29,6 +39,16 @@ namespace Octokit /// A of s starring the passed repository with star creation timestamps. Task> GetAllStargazersWithTimestamps(string owner, string name); + /// + /// Retrieves all of the stargazers for the passed repository with star creation timestamps. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of s starring the passed repository with star creation timestamps. + Task> GetAllStargazersWithTimestamps(string owner, string name, ApiOptions options); + /// /// Retrieves all of the starred (ies) for the current user. /// @@ -38,6 +58,16 @@ namespace Octokit /// Task> GetAllForCurrent(); + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current authenticated user. + /// + Task> GetAllForCurrent(ApiOptions options); + /// /// Retrieves all of the starred (ies) for the current user with star creation timestamps. /// @@ -47,6 +77,16 @@ namespace Octokit /// Task> GetAllForCurrentWithTimestamps(); + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current authenticated user with star creation timestamps. + /// + Task> GetAllForCurrentWithTimestamps(ApiOptions options); + /// /// Retrieves all of the starred (ies) for the current user. /// @@ -58,6 +98,18 @@ namespace Octokit /// Task> GetAllForCurrent(StarredRequest request); + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters. + /// + Task> GetAllForCurrent(StarredRequest request, ApiOptions options); + /// /// Retrieves all of the starred (ies) for the current user with star creation timestamps. /// @@ -69,6 +121,18 @@ namespace Octokit /// Task> GetAllForCurrentWithTimestamps(StarredRequest request); + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + Task> GetAllForCurrentWithTimestamps(StarredRequest request, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user. /// @@ -79,6 +143,17 @@ namespace Octokit /// Task> GetAllForUser(string user); + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A (ies) starred by the specified user. + /// + Task> GetAllForUser(string user, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. /// @@ -89,6 +164,17 @@ namespace Octokit /// Task> GetAllForUserWithTimestamps(string user); + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A (ies) starred by the specified user with star creation timestamps. + /// + Task> GetAllForUserWithTimestamps(string user, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user. /// @@ -98,6 +184,16 @@ namespace Octokit /// A starred by the specified user. Task> GetAllForUser(string user, StarredRequest request); + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A starred by the specified user. + Task> GetAllForUser(string user, StarredRequest request, ApiOptions options); + /// /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. /// @@ -110,6 +206,19 @@ namespace Octokit /// Task> GetAllForUserWithTimestamps(string user, StarredRequest request); + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the specified user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + Task> GetAllForUserWithTimestamps(string user, StarredRequest request, ApiOptions options); + /// /// Check if a repository is starred by the current authenticated user. /// diff --git a/Octokit/Clients/StarredClient.cs b/Octokit/Clients/StarredClient.cs index 132ca2dc..4ec54834 100644 --- a/Octokit/Clients/StarredClient.cs +++ b/Octokit/Clients/StarredClient.cs @@ -32,7 +32,24 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return ApiConnection.GetAll(ApiUrls.Stargazers(owner, name)); + return GetAllStargazers(owner, name, ApiOptions.None); + } + + /// + /// Retrieves all of the stargazers for the passed repository. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of s starring the passed repository. + public Task> GetAllStargazers(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Stargazers(owner, name), options); } /// @@ -47,7 +64,24 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return ApiConnection.GetAll(ApiUrls.Stargazers(owner, name), null, AcceptHeaders.StarCreationTimestamps); + return GetAllStargazersWithTimestamps(owner, name, ApiOptions.None); + } + + /// + /// Retrieves all of the stargazers for the passed repository with star creation timestamps. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A of s starring the passed repository with star creation timestamps. + public Task> GetAllStargazersWithTimestamps(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Stargazers(owner, name), null, AcceptHeaders.StarCreationTimestamps, options); } /// @@ -59,7 +93,22 @@ namespace Octokit /// public Task> GetAllForCurrent() { - return ApiConnection.GetAll(ApiUrls.Starred()); + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current authenticated user. + /// + public Task> GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Starred(), options); } /// @@ -71,7 +120,22 @@ namespace Octokit /// public Task> GetAllForCurrentWithTimestamps() { - return ApiConnection.GetAll(ApiUrls.Starred(), null, AcceptHeaders.StarCreationTimestamps); + return GetAllForCurrentWithTimestamps(ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current authenticated user with star creation timestamps. + /// + public Task> GetAllForCurrentWithTimestamps(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Starred(), null, AcceptHeaders.StarCreationTimestamps, options); } /// @@ -89,7 +153,25 @@ namespace Octokit { Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.Starred(), request.ToParametersDictionary()); + return GetAllForCurrent(request, ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters. + /// + public Task> GetAllForCurrent(StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Starred(), request.ToParametersDictionary(), options); } /// @@ -107,7 +189,25 @@ namespace Octokit { Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.Starred(), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps); + return GetAllForCurrentWithTimestamps(request, ApiOptions.None); + } + + /// + /// Retrieves all of the starred (ies) for the current user with star creation timestamps. + /// + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the current user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + public Task> GetAllForCurrentWithTimestamps(StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Starred(), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps, options); } /// @@ -122,7 +222,24 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(user, "user"); - return ApiConnection.GetAll(ApiUrls.StarredByUser(user)); + return GetAllForUser(user, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A (ies) starred by the specified user. + /// + public Task> GetAllForUser(string user, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.StarredByUser(user), options); } /// @@ -137,7 +254,24 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(user, "user"); - return ApiConnection.GetAll(ApiUrls.StarredByUser(user), null, AcceptHeaders.StarCreationTimestamps); + return GetAllForUserWithTimestamps(user, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A (ies) starred by the specified user with star creation timestamps. + /// + public Task> GetAllForUserWithTimestamps(string user, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.StarredByUser(user), null, AcceptHeaders.StarCreationTimestamps, options); } /// @@ -153,7 +287,24 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(user, "user"); Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.StarredByUser(user), request.ToParametersDictionary()); + return GetAllForUser(user, request, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// A starred by the specified user. + public Task> GetAllForUser(string user, StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.StarredByUser(user), request.ToParametersDictionary(), options); } /// @@ -172,7 +323,27 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(user, "user"); Ensure.ArgumentNotNull(request, "request"); - return ApiConnection.GetAll(ApiUrls.StarredByUser(user), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps); + return GetAllForUserWithTimestamps(user, request, ApiOptions.None); + } + + /// + /// Retrieves all of the (ies) starred by the specified user with star creation timestamps. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Options for changing the API response + /// Thrown if the client is not authenticated. + /// + /// A of (ies) starred by the specified user, + /// sorted according to the passed request parameters and with star creation timestamps. + /// + public Task> GetAllForUserWithTimestamps(string user, StarredRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.StarredByUser(user), request.ToParametersDictionary(), AcceptHeaders.StarCreationTimestamps, options); } /// From f05f6dcab9ffed84570a6e5e49d1b3f1851d484d Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Fri, 3 Jun 2016 00:39:55 -0700 Subject: [PATCH 6/6] Add support for code searches without a search term (#1338) --- .../Clients/SearchClientTests.cs | 14 ++++++++++++++ Octokit/Models/Request/BaseSearchRequest.cs | 9 ++++++++- Octokit/Models/Request/SearchCodeRequest.cs | 8 ++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index 8ae98250..b1267410 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -57,6 +57,20 @@ public class SearchClientTests Assert.NotEmpty(repos.Items); } + [IntegrationTest] + public async Task SearchForFileNameInCodeWithoutTerm() + { + var request = new SearchCodeRequest() + { + FileName = "readme.md", + Repos = new RepositoryCollection { "octokit/octokit.net" } + }; + + var repos = await _gitHubClient.Search.SearchCode(request); + + Assert.NotEmpty(repos.Items); + } + [IntegrationTest] public async Task SearchForWordInCode() { diff --git a/Octokit/Models/Request/BaseSearchRequest.cs b/Octokit/Models/Request/BaseSearchRequest.cs index d3cff73d..7ff689de 100644 --- a/Octokit/Models/Request/BaseSearchRequest.cs +++ b/Octokit/Models/Request/BaseSearchRequest.cs @@ -85,7 +85,14 @@ namespace Octokit get { var mergedParameters = string.Join("+", MergedQualifiers()); - return Term + (mergedParameters.IsNotBlank() ? "+" + mergedParameters : ""); + if (string.IsNullOrEmpty(Term)) + { + return mergedParameters; + } + else + { + return Term + (mergedParameters.IsNotBlank() ? "+" + mergedParameters : ""); + } } } diff --git a/Octokit/Models/Request/SearchCodeRequest.cs b/Octokit/Models/Request/SearchCodeRequest.cs index f5c8693d..4e414231 100644 --- a/Octokit/Models/Request/SearchCodeRequest.cs +++ b/Octokit/Models/Request/SearchCodeRequest.cs @@ -15,6 +15,14 @@ namespace Octokit [DebuggerDisplay("{DebuggerDisplay,nq}")] public class SearchCodeRequest : BaseSearchRequest { + /// + /// Initializes a new instance of the class. + /// + public SearchCodeRequest() : base() + { + Repos = new RepositoryCollection(); + } + /// /// Initializes a new instance of the class. ///