From 2979b1009c8692c040678b3d136b4f9e6dfd33c8 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 6 Nov 2013 22:17:09 +1000 Subject: [PATCH 1/2] Implement Observable Organization Members API --- .../IObservableOrganizationMembersClient.cs | 106 ++++++++++++ .../ObservableOrganizationMembersClient.cs | 156 ++++++++++++++++++ Octokit.Reactive/Octokit.Reactive.csproj | 2 + Octokit/Clients/OrganizationMembersClient.cs | 4 +- Octokit/Helpers/ApiUrls.cs | 20 +++ 5 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs diff --git a/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs b/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs new file mode 100644 index 00000000..aa803168 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit.Reactive +{ + public interface IObservableOrganizationMembersClient + { + /// + /// + /// 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. + /// + /// + /// + IObservable GetAll(string org); + + /// + /// List all users who have publicized their membership of the organization. + /// + /// http://developer.github.com/v3/orgs/members/#public-members-list + /// + /// + IObservable GetPublic(string org); + + /// + /// Check if a user is, publicly or privately, a member of the organization. + /// + /// + /// See the API documentation + /// for more information. + /// + /// + /// + /// + IObservable CheckMember(string org, string user); + + /// + /// Check is a user is publicly a member of the organization. + /// + /// + /// See the API documentation + /// for more information. + /// + /// + /// + /// + IObservable CheckMemberPublic(string org, string user); + + /// + /// Removes a user from the organization, this will also remove them from all teams + /// within the organization and they will no longer have any access to the organization's + /// repositories. + /// + /// + /// See the API documentation + /// for more information. + /// + /// + /// + /// + IObservable Delete(string org, string user); + + /// + /// Make the authenticated user's organization membership public. + /// + /// + /// This method requires authentication. + /// See the API documentation + /// for more information. + /// + /// + /// + /// + IObservable Publicize(string org, string user); + + /// + /// Make the authenticated user's organization membership private. + /// + /// + /// This method requries authentication. + /// See the API documentation + /// for more information. + /// + /// + /// + /// + IObservable Conceal(string org, string user); + } +} diff --git a/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs new file mode 100644 index 00000000..2aa18de2 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Reactive; +using System.Reactive.Threading.Tasks; +using Octokit.Reactive.Internal; + +namespace Octokit.Reactive +{ + public class ObservableOrganizationMembersClient : IObservableOrganizationMembersClient + { + readonly IOrganizationMembersClient _client; + readonly IConnection _connection; + + public ObservableOrganizationMembersClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Organization.Member; + _connection = client.Connection; + } + + /// + /// + /// 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. + /// + /// + /// + public IObservable GetAll(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Members(org)); + } + + /// + /// List all users who have publicized their membership of the organization. + /// + /// http://developer.github.com/v3/orgs/members/#public-members-list + /// + /// + public IObservable GetPublic(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + + return _connection.GetAndFlattenAllPages(ApiUrls.PublicMembers(org)); + } + + /// + /// Check if a user is, publicly or privately, a member of the organization. + /// + /// + /// See the API documentation + /// for more information. + /// + /// + /// + /// + public IObservable CheckMember(string org, string user) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + + return _client.CheckMember(org, user).ToObservable(); + } + + /// + /// Check is a user is publicly a member of the organization. + /// + /// + /// See the API documentation + /// for more information. + /// + /// + /// + /// + public IObservable CheckMemberPublic(string org, string user) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + + return _client.CheckMemberPublic(org, user).ToObservable(); + } + + /// + /// Removes a user from the organization, this will also remove them from all teams + /// within the organization and they will no longer have any access to the organization's + /// repositories. + /// + /// + /// See the API documentation + /// for more information. + /// + /// + /// + /// + public IObservable Delete(string org, string user) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + + return _client.Delete(org, user).ToObservable(); + } + + /// + /// Make the authenticated user's organization membership public. + /// + /// + /// This method requires authentication. + /// See the API documentation + /// for more information. + /// + /// + /// + /// + public IObservable Publicize(string org, string user) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + + return _client.Publicize(org, user).ToObservable(); + } + + /// + /// Make the authenticated user's organization membership private. + /// + /// + /// This method requries authentication. + /// See the API documentation + /// for more information. + /// + /// + /// + /// + public IObservable Conceal(string org, string user) + { + Ensure.ArgumentNotNullOrEmptyString(org, "org"); + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + + return _client.Conceal(org, user).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 493f29cc..626d87f2 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -75,6 +75,7 @@ + @@ -92,6 +93,7 @@ + diff --git a/Octokit/Clients/OrganizationMembersClient.cs b/Octokit/Clients/OrganizationMembersClient.cs index a018008f..dcc4aa71 100644 --- a/Octokit/Clients/OrganizationMembersClient.cs +++ b/Octokit/Clients/OrganizationMembersClient.cs @@ -38,7 +38,7 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return ApiConnection.GetAll("orgs/{0}/members".FormatUri(org)); + return ApiConnection.GetAll(ApiUrls.Members(org)); } /// @@ -51,7 +51,7 @@ namespace Octokit { Ensure.ArgumentNotNullOrEmptyString(org, "org"); - return ApiConnection.GetAll("orgs/{0}/public_members".FormatUri(org)); + return ApiConnection.GetAll(ApiUrls.PublicMembers(org)); } /// diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 0b4bdc61..734cddce 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -247,6 +247,26 @@ namespace Octokit return "repos/{0}/{1}/assignees/{2}".FormatUri(owner, name, login); } + /// + /// Returns the that returns all of the members of the organization + /// + /// The organization + /// + public static Uri Members(string org) + { + return "orgs/{0}/members".FormatUri(org); + } + + /// + /// Returns the that returns all of the public members of the organization + /// + /// Organization + /// + public static Uri PublicMembers(string org) + { + return "orgs/{0}/public_members".FormatUri(org); + } + /// /// Returns the that returns a 204 if requester is an organization member and /// the user is, publicly or privately a member of the organization. From d42bf57e954e534e6e373764e6cc9dc818302d60 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Thu, 7 Nov 2013 17:36:19 +1000 Subject: [PATCH 2/2] Add unit tests --- Octokit.Tests/Octokit.Tests.csproj | 1 + ...bservableOrganizationMembersClientTests.cs | 189 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 6f2f5ee2..c29287f3 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -116,6 +116,7 @@ + diff --git a/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs b/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs new file mode 100644 index 00000000..9b27ea7f --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs @@ -0,0 +1,189 @@ +using NSubstitute; +using Octokit; +using Octokit.Internal; +using Octokit.Reactive; +using Octokit.Tests.Helpers; +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableOrganizationMembersClientTests + { + public class TheGetAllMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.GetAll("org"); + + gitHubClient.Connection.GetAsync>( + new Uri("orgs/org/members", UriKind.Relative), null, null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.GetAll(null)); + await AssertEx.Throws(async () => await client.GetAll("")); + } + } + + public class TheGetPublicMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.GetPublic("org"); + + gitHubClient.Connection.GetAsync>( + new Uri("orgs/org/public_members", UriKind.Relative), null, null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.GetPublic(null)); + await AssertEx.Throws(async () => await client.GetPublic("")); + } + } + + public class TheCheckMemberMethod + { + [Fact] + public void ChecksMemberFromClientOrganizationMember() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.CheckMember("org", "user"); + + gitHubClient.Organization.Member.Received().CheckMember("org", "user"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.CheckMember(null, "username")); + await AssertEx.Throws(async () => await client.CheckMember("", "username")); + await AssertEx.Throws(async () => await client.CheckMember("org", null)); + await AssertEx.Throws(async () => await client.CheckMember("org", "")); + } + } + + public class TheCheckMemberPublicMethod + { + [Fact] + public void ChecksMemberPublicFromClientOrganizationMember() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.CheckMemberPublic("org", "user"); + + gitHubClient.Organization.Member.Received().CheckMemberPublic("org", "user"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.CheckMemberPublic(null, "username")); + await AssertEx.Throws(async () => await client.CheckMemberPublic("", "username")); + await AssertEx.Throws(async () => await client.CheckMemberPublic("org", null)); + await AssertEx.Throws(async () => await client.CheckMemberPublic("org", "")); + } + } + + public class TheDeleteMethod + { + [Fact] + public void DeletesFromClientOrganizationMember() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.Delete("org", "user"); + + gitHubClient.Organization.Member.Received().Delete("org", "user"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.Delete(null, "username")); + await AssertEx.Throws(async () => await client.Delete("", "username")); + await AssertEx.Throws(async () => await client.Delete("org", null)); + await AssertEx.Throws(async () => await client.Delete("org", "")); + } + } + + public class ThePublicizeMethod + { + [Fact] + public void PublicizeFromClientOrganizationMember() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.Publicize("org", "user"); + + gitHubClient.Organization.Member.Received().Publicize("org", "user"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.Publicize(null, "username")); + await AssertEx.Throws(async () => await client.Publicize("", "username")); + await AssertEx.Throws(async () => await client.Publicize("org", null)); + await AssertEx.Throws(async () => await client.Publicize("org", "")); + } + } + + public class TheConcealMethod + { + [Fact] + public void ConcealFromClientOrganizationMember() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.Conceal("org", "user"); + + gitHubClient.Organization.Member.Received().Conceal("org", "user"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.Conceal(null, "username")); + await AssertEx.Throws(async () => await client.Conceal("", "username")); + await AssertEx.Throws(async () => await client.Conceal("org", null)); + await AssertEx.Throws(async () => await client.Conceal("org", "")); + } + } + } +}