diff --git a/Octokit.Reactive/Clients/IObservableFollowersClient.cs b/Octokit.Reactive/Clients/IObservableFollowersClient.cs index 46a38fc1ea..32030bc13d 100644 --- a/Octokit.Reactive/Clients/IObservableFollowersClient.cs +++ b/Octokit.Reactive/Clients/IObservableFollowersClient.cs @@ -16,6 +16,17 @@ public interface IObservableFollowersClient [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] IObservable GetAllForCurrent(); + /// + /// List the authenticated user’s followers + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that follow the authenticated user. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + IObservable GetAllForCurrent(ApiOptions options); + /// /// List a user’s followers /// @@ -26,6 +37,17 @@ public interface IObservableFollowersClient /// A of s that follow the passed user. IObservable GetAll(string login); + /// + /// List a user’s followers + /// + /// Options for changing the API response + /// The login name for the user + /// + /// See the API documentation for more information. + /// + /// A of s that follow the passed user. + IObservable GetAll(string login, ApiOptions options); + /// /// List who the authenticated user is following /// @@ -36,6 +58,16 @@ public interface IObservableFollowersClient [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] IObservable GetAllFollowingForCurrent(); + /// + /// List who the authenticated user is following + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the authenticated user follows. + IObservable GetAllFollowingForCurrent(ApiOptions options); + /// /// List who a user is following /// @@ -46,6 +78,17 @@ public interface IObservableFollowersClient /// A of s that the passed user follows. IObservable GetAllFollowing(string login); + /// + /// List who a user is following + /// + /// The login name of the user + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the passed user follows. + IObservable GetAllFollowing(string login, ApiOptions options); + /// /// Check if the authenticated user follows another user /// diff --git a/Octokit.Reactive/Clients/ObservableFollowersClient.cs b/Octokit.Reactive/Clients/ObservableFollowersClient.cs index c77b457127..955b530927 100644 --- a/Octokit.Reactive/Clients/ObservableFollowersClient.cs +++ b/Octokit.Reactive/Clients/ObservableFollowersClient.cs @@ -31,7 +31,22 @@ public ObservableFollowersClient(IGitHubClient client) /// A of s that follow the authenticated user. public IObservable GetAllForCurrent() { - return _connection.GetAndFlattenAllPages(ApiUrls.Followers()); + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// List the authenticated user’s followers + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that follow the authenticated user. + public IObservable GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Followers(), options); } /// @@ -46,7 +61,24 @@ public IObservable GetAll(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - return _connection.GetAndFlattenAllPages(ApiUrls.Followers(login)); + return GetAll(login, ApiOptions.None); + } + + /// + /// List a user’s followers + /// + /// The login name for the user + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that follow the passed user. + public IObservable GetAll(string login, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Followers(login), options); } /// @@ -58,7 +90,22 @@ public IObservable GetAll(string login) /// A of s that the authenticated user follows. public IObservable GetAllFollowingForCurrent() { - return _connection.GetAndFlattenAllPages(ApiUrls.Following()); + return GetAllFollowingForCurrent(ApiOptions.None); + } + + /// + /// List who the authenticated user is following + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the authenticated user follows. + public IObservable GetAllFollowingForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Following(), options); } /// @@ -73,7 +120,24 @@ public IObservable GetAllFollowing(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - return _connection.GetAndFlattenAllPages(ApiUrls.Following(login)); + return GetAllFollowing(login, ApiOptions.None); + } + + /// + /// List who a user is following + /// + /// The login name of the user + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the passed user follows. + public IObservable GetAllFollowing(string login, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Following(login), options); } /// diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 3192936c80..2c97237113 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -137,6 +137,7 @@ + diff --git a/Octokit.Tests.Integration/Reactive/ObservableFollowersClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableFollowersClientTests.cs new file mode 100644 index 0000000000..6b3e61dc8d --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableFollowersClientTests.cs @@ -0,0 +1,305 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableFollowersClientTests + { + public class TheGetAllForCurrentMethod + { + readonly ObservableFollowersClient _followersClient; + + public TheGetAllForCurrentMethod() + { + var github = Helper.GetAuthenticatedClient(); + + _followersClient = new ObservableFollowersClient(github); + } + + [IntegrationTest] + public async Task ReturnsFollowers() + { + var followers = await _followersClient.GetAllForCurrent().ToList(); + + Assert.NotEmpty(followers); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowersWithoutStart() + { + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var followers = await _followersClient.GetAllForCurrent(options).ToList(); + + Assert.Equal(1, followers.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowersWithStart() + { + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + var followers = await _followersClient.GetAllForCurrent(options).ToList(); + + Assert.Equal(1, followers.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstFollowersPage = await _followersClient.GetAllForCurrent(startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondFollowersPage = await _followersClient.GetAllForCurrent(skipStartOptions).ToList(); + + Assert.NotEqual(firstFollowersPage[0].Id, secondFollowersPage[0].Id); + } + } + + public class TheGetAllMethod + { + readonly ObservableFollowersClient _followersClient; + const string login = "samthedev"; + + public TheGetAllMethod() + { + var github = Helper.GetAuthenticatedClient(); + + _followersClient = new ObservableFollowersClient(github); + } + + [IntegrationTest] + public async Task ReturnsFollowers() + { + var followers = await _followersClient.GetAll(login).ToList(); + + Assert.NotEmpty(followers); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowersWithoutStart() + { + var options = new ApiOptions + { + PageSize = 3, + PageCount = 1 + }; + + var followers = await _followersClient.GetAll(login, options).ToList(); + + Assert.Equal(3, followers.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowersWithStart() + { + var options = new ApiOptions + { + PageSize = 2, + PageCount = 1, + StartPage = 1 + }; + + var followers = await _followersClient.GetAll(login, options).ToList(); + + Assert.Equal(2, followers.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 2, + PageCount = 1 + }; + + var firstFollowersPage = await _followersClient.GetAll(login, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 2, + PageCount = 1, + StartPage = 2 + }; + + var secondFollowersPage = await _followersClient.GetAll(login, skipStartOptions).ToList(); + + Assert.NotEqual(firstFollowersPage[0].Id, secondFollowersPage[0].Id); + Assert.NotEqual(firstFollowersPage[1].Id, secondFollowersPage[1].Id); + } + } + + public class TheGetAllFollowingForCurrentMethod + { + readonly ObservableFollowersClient _followersClient; + + public TheGetAllFollowingForCurrentMethod() + { + var github = Helper.GetAuthenticatedClient(); + + _followersClient = new ObservableFollowersClient(github); + } + + [IntegrationTest] + public async Task ReturnsFollowing() + { + var following = await _followersClient.GetAllFollowingForCurrent().ToList(); + + Assert.NotEmpty(following); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowingWithoutStart() + { + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var following = await _followersClient.GetAllFollowingForCurrent(options).ToList(); + + Assert.Equal(1, following.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowingWithStart() + { + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + var following = await _followersClient.GetAllFollowingForCurrent(options).ToList(); + + Assert.Equal(1, following.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstFollowingPage = await _followersClient.GetAllFollowingForCurrent(startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondFollowingPage = await _followersClient.GetAllFollowingForCurrent(skipStartOptions).ToList(); + + Assert.NotEqual(firstFollowingPage[0].Id, secondFollowingPage[0].Id); + } + } + + public class TheGetAllFollowingMethod + { + readonly ObservableFollowersClient _followersClient; + const string login = "samthedev"; + + public TheGetAllFollowingMethod() + { + var github = Helper.GetAuthenticatedClient(); + + _followersClient = new ObservableFollowersClient(github); + } + + [IntegrationTest] + public async Task ReturnsFollowing() + { + var following = await _followersClient.GetAllFollowing(login).ToList(); + + Assert.NotEmpty(following); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowingWithoutStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var following = await _followersClient.GetAllFollowing(login, options).ToList(); + + Assert.Equal(5, following.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfFollowingWithStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 1 + }; + + var following = await _followersClient.GetAllFollowing(login, options).ToList(); + + Assert.Equal(5, following.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var firstFollowingPage = await _followersClient.GetAllFollowing(login, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var secondFollowingPage = await _followersClient.GetAllFollowing(login, skipStartOptions).ToList(); + + Assert.NotEqual(firstFollowingPage[0].Id, secondFollowingPage[0].Id); + Assert.NotEqual(firstFollowingPage[1].Id, secondFollowingPage[1].Id); + Assert.NotEqual(firstFollowingPage[2].Id, secondFollowingPage[2].Id); + Assert.NotEqual(firstFollowingPage[3].Id, secondFollowingPage[3].Id); + Assert.NotEqual(firstFollowingPage[4].Id, secondFollowingPage[4].Id); + } + } + } +} diff --git a/Octokit.Tests/Clients/FollowersClientTests.cs b/Octokit.Tests/Clients/FollowersClientTests.cs index f9201f6d8d..921b1a2f4f 100644 --- a/Octokit.Tests/Clients/FollowersClientTests.cs +++ b/Octokit.Tests/Clients/FollowersClientTests.cs @@ -37,7 +37,35 @@ public void RequestsTheCorrectUrl() client.GetAllForCurrent(); connection.Received().GetAll( - Arg.Is(u => u.ToString() == "user/followers")); + Arg.Is(u => u.ToString() == "user/followers"),Args.ApiOptions); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new FollowersClient(connection); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + client.GetAllForCurrent(options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "user/followers"),options); + } + + [Fact] + public async Task EnsureNonNullArguments() + { + var connection = Substitute.For(); + var client = new FollowersClient(connection); + + await Assert.ThrowsAsync(() => client.GetAllForCurrent(null)); } } @@ -52,7 +80,26 @@ public void RequestsTheCorrectUrl() client.GetAll("alfhenrik"); connection.Received().GetAll( - Arg.Is(u => u.ToString() == "users/alfhenrik/followers")); + Arg.Is(u => u.ToString() == "users/alfhenrik/followers"), Args.ApiOptions); + } + + [Fact] + public void RequestsTheCorrectUrlWithOptions() + { + var connection = Substitute.For(); + var client = new FollowersClient(connection); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + client.GetAll("alfhenrik",options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "users/alfhenrik/followers"), options); } [Fact] @@ -63,10 +110,12 @@ public async Task EnsureNonNullArguments() await Assert.ThrowsAsync(() => client.GetAll(null)); await Assert.ThrowsAsync(() => client.GetAll("")); + await Assert.ThrowsAsync(() => client.GetAll("fake",null)); + await Assert.ThrowsAsync(() => client.GetAll("",ApiOptions.None)); } } - public class TheGetFollowingForCurrentMethod + public class TheGetAllFollowingForCurrentMethod { [Fact] public void RequestsTheCorrectUrl() @@ -76,11 +125,38 @@ public void RequestsTheCorrectUrl() client.GetAllFollowingForCurrent(); - connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/following")); + connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/following"), Args.ApiOptions); + } + + [Fact] + public void RequestsTheCorrectUrlWithOptions() + { + var connection = Substitute.For(); + var client = new FollowersClient(connection); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + client.GetAllFollowingForCurrent(options); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/following"), options); + } + + [Fact] + public async Task EnsureNonNullArguments() + { + var connection = Substitute.For(); + var client = new FollowersClient(connection); + + await Assert.ThrowsAsync(() => client.GetAllFollowingForCurrent(null)); } } - public class TheGetFollowingMethod + public class TheGetAllFollowingMethod { [Fact] public void RequestsTheCorrectUrl() @@ -90,7 +166,25 @@ public void RequestsTheCorrectUrl() client.GetAllFollowing("alfhenrik"); - connection.Received().GetAll(Arg.Is(u => u.ToString() == "users/alfhenrik/following")); + connection.Received().GetAll(Arg.Is(u => u.ToString() == "users/alfhenrik/following"), Args.ApiOptions); + } + + [Fact] + public void RequestsTheCorrectUrlWithOptions() + { + var connection = Substitute.For(); + var client = new FollowersClient(connection); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + client.GetAllFollowing("alfhenrik", options); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "users/alfhenrik/following"), options); } [Fact] @@ -101,6 +195,8 @@ public async Task EnsuresNonNullArguments() await Assert.ThrowsAsync(() => client.GetAllFollowing(null)); await Assert.ThrowsAsync(() => client.GetAllFollowing("")); + await Assert.ThrowsAsync(() => client.GetAllFollowing("fake", null)); + await Assert.ThrowsAsync(() => client.GetAllFollowing("", ApiOptions.None)); } } diff --git a/Octokit.Tests/Reactive/ObservableFollowersTest.cs b/Octokit.Tests/Reactive/ObservableFollowersTest.cs index b7bd269d71..b44807e688 100644 --- a/Octokit.Tests/Reactive/ObservableFollowersTest.cs +++ b/Octokit.Tests/Reactive/ObservableFollowersTest.cs @@ -25,7 +25,7 @@ public void RequestsTheCorrectUrl() client.GetAllForCurrent(); githubClient.Connection.Received(1).Get>( - new Uri("user/followers", UriKind.Relative), null, null); + new Uri("user/followers", UriKind.Relative), Args.EmptyDictionary, null); } } @@ -40,7 +40,7 @@ public void RequestsTheCorrectUrl() client.GetAll("alfhenrik"); githubClient.Connection.Received(1).Get>( - new Uri("users/alfhenrik/followers", UriKind.Relative), null, null); + new Uri("users/alfhenrik/followers", UriKind.Relative), Args.EmptyDictionary, null); } [Fact] @@ -64,7 +64,7 @@ public void RequestsTheCorrectUrl() client.GetAllFollowingForCurrent(); githubClient.Connection.Received(1).Get>( - new Uri("user/following", UriKind.Relative), null, null); + new Uri("user/following", UriKind.Relative), Args.EmptyDictionary, null); } } @@ -79,7 +79,7 @@ public void RequestsTheCorrectUrl() client.GetAllFollowing("alfhenrik"); githubClient.Connection.Received(1).Get>( - new Uri("users/alfhenrik/following", UriKind.Relative), null, null); + new Uri("users/alfhenrik/following", UriKind.Relative), Args.EmptyDictionary, null); } [Fact] diff --git a/Octokit/Clients/FollowersClient.cs b/Octokit/Clients/FollowersClient.cs index b2cfe95ff5..d1295c2b24 100644 --- a/Octokit/Clients/FollowersClient.cs +++ b/Octokit/Clients/FollowersClient.cs @@ -29,7 +29,22 @@ public FollowersClient(IApiConnection apiConnection) : base(apiConnection) /// A of s that follow the authenticated user. public Task> GetAllForCurrent() { - return ApiConnection.GetAll(ApiUrls.Followers()); + return GetAllForCurrent(ApiOptions.None); + } + + /// + /// List the authenticated user’s followers + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that follow the authenticated user. + public Task> GetAllForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Followers(),options); } /// @@ -44,7 +59,24 @@ public Task> GetAll(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - return ApiConnection.GetAll(ApiUrls.Followers(login)); + return GetAll(login, ApiOptions.None); + } + + /// + /// List a user’s followers + /// + /// The login name for the user + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that follow the passed user. + public Task> GetAll(string login, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Followers(login), options); } /// @@ -56,7 +88,22 @@ public Task> GetAll(string login) /// A of s that the authenticated user follows. public Task> GetAllFollowingForCurrent() { - return ApiConnection.GetAll(ApiUrls.Following()); + return GetAllFollowingForCurrent(ApiOptions.None); + } + + /// + /// List who the authenticated user is following + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the authenticated user follows. + public Task> GetAllFollowingForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Following(), options); } /// @@ -71,7 +118,24 @@ public Task> GetAllFollowing(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - return ApiConnection.GetAll(ApiUrls.Following(login)); + return GetAllFollowing(login, ApiOptions.None); + } + + /// + /// List who a user is following + /// + /// The login name of the user + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the passed user follows. + public Task> GetAllFollowing(string login, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Following(login), options); } /// diff --git a/Octokit/Clients/IFollowersClient.cs b/Octokit/Clients/IFollowersClient.cs index 00f7b7e01b..0c7dee8a02 100644 --- a/Octokit/Clients/IFollowersClient.cs +++ b/Octokit/Clients/IFollowersClient.cs @@ -22,6 +22,16 @@ public interface IFollowersClient [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] Task> GetAllForCurrent(); + /// + /// List the authenticated user’s followers + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that follow the authenticated user. + Task> GetAllForCurrent(ApiOptions options); + /// /// List a user’s followers /// @@ -32,6 +42,17 @@ public interface IFollowersClient /// A of s that follow the passed user. Task> GetAll(string login); + /// + /// List a user’s followers + /// + /// The login name for the user + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that follow the passed user. + Task> GetAll(string login, ApiOptions options); + /// /// List who the authenticated user is following /// @@ -42,6 +63,16 @@ public interface IFollowersClient [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] Task> GetAllFollowingForCurrent(); + /// + /// List who the authenticated user is following + /// + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the authenticated user follows. + Task> GetAllFollowingForCurrent(ApiOptions options); + /// /// List who a user is following /// @@ -52,6 +83,17 @@ public interface IFollowersClient /// A of s that the passed user follows. Task> GetAllFollowing(string login); + /// + /// List who a user is following + /// + /// The login name of the user + /// Options for changing the API response + /// + /// See the API documentation for more information. + /// + /// A of s that the passed user follows. + Task> GetAllFollowing(string login, ApiOptions options); + /// /// Check if the authenticated user follows another user ///