From bd10379846ace99266788cd6c7c71a7630864fed Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sun, 14 Feb 2016 23:17:06 +1000 Subject: [PATCH] Add Reactive implementation and unit tests --- .../IObservableUserAdministrationClient.cs | 101 ++++++++- .../ObservableUserAdministrationClient.cs | 128 ++++++++++- .../ObservableGithubClientExtensions.cs | 14 ++ .../Octokit.Tests.Integration.csproj | 1 + .../ObservableEnterpriseLdapClientTests.cs | 5 +- ...ObservableUserAdministrationClientTests.cs | 201 ++++++++++++++++++ ...ObservableUserAdministrationClientTests.cs | 122 ++++++++++- 7 files changed, 548 insertions(+), 24 deletions(-) create mode 100644 Octokit.Tests.Integration/Reactive/ObservableUserAdministrationClientTests.cs diff --git a/Octokit.Reactive/Clients/IObservableUserAdministrationClient.cs b/Octokit.Reactive/Clients/IObservableUserAdministrationClient.cs index d893f91d8c..844a0e1598 100644 --- a/Octokit.Reactive/Clients/IObservableUserAdministrationClient.cs +++ b/Octokit.Reactive/Clients/IObservableUserAdministrationClient.cs @@ -5,7 +5,7 @@ namespace Octokit.Reactive { /// - /// A client for GitHub's User Administration API. + /// A client for GitHub's User Administration API (GitHub Enterprise) /// /// /// See the Administration API documentation for more details. @@ -13,43 +13,126 @@ namespace Octokit.Reactive public interface IObservableUserAdministrationClient { /// - /// Promotes ordinary user to a site administrator. + /// Create a new user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#promote-an-ordinary-user-to-a-site-administrator + /// See the API documentation + /// for more information. + /// + /// The object describing the user to create + /// The created object + IObservable Create(NewUser newUser); + + /// + /// Rename an existing user (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// Note that this queues a request to rename a user, rather than execute it straight away + /// + /// The username to rename + /// The request, specifying the new login + /// A object indicating the queued task message and Url to the user + IObservable Rename(string login, UserRename userRename); + + /// + /// Create an impersonation OAuth token (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to impersonate + /// The request specifying the required scopes + /// An object containing the impersonation token + IObservable CreateImpersonationToken(string login, NewImpersonationToken newImpersonationToken); + + /// + /// Deletes an impersonation OAuth token (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to remove impersonation token from + /// + IObservable DeleteImpersonationToken(string login); + + /// + /// Promotes ordinary user to a site administrator (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. /// /// The user to promote to administrator. /// IObservable Promote(string login); /// - /// Demotes a site administrator to an ordinary user. + /// Demotes a site administrator to an ordinary user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#demote-a-site-administrator-to-an-ordinary-user + /// See the API documentation + /// for more information. /// /// The user to demote from administrator. /// IObservable Demote(string login); /// - /// Suspends a user. + /// Suspends a user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#suspend-a-user + /// See the API documentation + /// for more information. /// /// The user to suspend. /// IObservable Suspend(string login); /// - /// Unsuspends a user. + /// Unsuspends a user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#unsuspend-a-user + /// See the API documentation + /// for more information. /// /// The user to unsuspend. /// IObservable Unsuspend(string login); + + /// + /// List all public keys (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// + IObservable ListAllPublicKeys(); + + /// + /// Delete a user (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to delete + /// + IObservable Delete(string login); + + /// + /// Delete a public key (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The key to delete + /// + IObservable DeletePublicKey(int keyId); } } diff --git a/Octokit.Reactive/Clients/ObservableUserAdministrationClient.cs b/Octokit.Reactive/Clients/ObservableUserAdministrationClient.cs index d76d7e9dad..bc0bf672e7 100644 --- a/Octokit.Reactive/Clients/ObservableUserAdministrationClient.cs +++ b/Octokit.Reactive/Clients/ObservableUserAdministrationClient.cs @@ -7,9 +7,16 @@ namespace Octokit.Reactive { + /// + /// A client for GitHub's User Administration API (GitHub Enterprise) + /// + /// + /// See the Administration API documentation for more details. + /// public class ObservableUserAdministrationClient : IObservableUserAdministrationClient { readonly IUserAdministrationClient _client; + readonly IConnection _connection; /// /// Initializes a new instance of the class. @@ -20,13 +27,74 @@ public ObservableUserAdministrationClient(IGitHubClient client) Ensure.ArgumentNotNull(client, "client"); _client = client.User.Administration; + _connection = client.Connection; } /// - /// Promotes ordinary user to a site administrator. + /// Create a new user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#promote-an-ordinary-user-to-a-site-administrator + /// See the API documentation + /// for more information. + /// + /// The object describing the user to create + /// The created object + public IObservable Create(NewUser newUser) + { + return _client.Create(newUser).ToObservable(); + } + + /// + /// Rename an existing user (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// Note that this queues a request to rename a user, rather than execute it straight away + /// + /// The username to rename + /// The request, specifying the new login + /// A object indicating the queued task message and Url to the user + public IObservable Rename(string login, UserRename userRename) + { + return _client.Rename(login, userRename).ToObservable(); + } + + /// + /// Create an impersonation OAuth token (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to impersonate + /// The request specifying the required scopes + /// An object containing the impersonation token + public IObservable CreateImpersonationToken(string login, NewImpersonationToken newImpersonationToken) + { + return _client.CreateImpersonationToken(login, newImpersonationToken).ToObservable(); + } + + /// + /// Deletes an impersonation OAuth token (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to remove impersonation token from + /// + public IObservable DeleteImpersonationToken(string login) + { + return _client.DeleteImpersonationToken(login).ToObservable(); + } + + /// + /// Promotes ordinary user to a site administrator (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. /// /// The user to promote to administrator. /// @@ -36,10 +104,11 @@ public IObservable Promote(string login) } /// - /// Demotes a site administrator to an ordinary user. + /// Demotes a site administrator to an ordinary user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#demote-a-site-administrator-to-an-ordinary-user + /// See the API documentation + /// for more information. /// /// The user to demote from administrator. /// @@ -49,10 +118,11 @@ public IObservable Demote(string login) } /// - /// Suspends a user. + /// Suspends a user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#suspend-a-user + /// See the API documentation + /// for more information. /// /// The user to suspend. /// @@ -62,10 +132,11 @@ public IObservable Suspend(string login) } /// - /// Unsuspends a user. + /// Unsuspends a user (must be Site Admin user). /// /// - /// https://developer.github.com/v3/users/administration/#unsuspend-a-user + /// See the API documentation + /// for more information. /// /// The user to unsuspend. /// @@ -73,5 +144,46 @@ public IObservable Unsuspend(string login) { return _client.Unsuspend(login).ToObservable(); } + + /// + /// List all public keys (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// + public IObservable ListAllPublicKeys() + { + return _connection.GetAndFlattenAllPages(ApiUrls.UserAdministrationPublicKeys()); + } + + /// + /// Delete a user (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to delete + /// + public IObservable Delete(string login) + { + return _client.Delete(login).ToObservable(); + } + + /// + /// Delete a public key (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The key to delete + /// + public IObservable DeletePublicKey(int keyId) + { + return _client.DeletePublicKey(keyId).ToObservable(); + } } } diff --git a/Octokit.Tests.Integration/Helpers/ObservableGithubClientExtensions.cs b/Octokit.Tests.Integration/Helpers/ObservableGithubClientExtensions.cs index 8a5919ade6..030795b0d0 100644 --- a/Octokit.Tests.Integration/Helpers/ObservableGithubClientExtensions.cs +++ b/Octokit.Tests.Integration/Helpers/ObservableGithubClientExtensions.cs @@ -27,5 +27,19 @@ internal async static Task CreateRepositoryContext(this IObse return new RepositoryContext(repo); } + + internal async static Task CreateEnterpriseTeamContext(this IObservableGitHubClient client, string organization, NewTeam newTeam) + { + var team = await client.Organization.Team.Create(organization, newTeam); + + return new EnterpriseTeamContext(team); + } + + internal async static Task CreateEnterpriseUserContext(this IObservableGitHubClient client, NewUser newUser) + { + var user = await client.User.Administration.Create(newUser); + + return new EnterpriseUserContext(user); + } } } \ No newline at end of file diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 9e3f07e736..3900738aca 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -145,6 +145,7 @@ + diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index 0880616259..dde932d8c9 100644 --- a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -20,11 +20,10 @@ public class ObservableEnterpriseLdapClientTests : IDisposable public ObservableEnterpriseLdapClientTests() { - var gitHub = EnterpriseHelper.GetAuthenticatedClient(); - _github = new ObservableGitHubClient(gitHub); + _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; - _context = gitHub.CreateEnterpriseTeamContext(EnterpriseHelper.Organization, newTeam).Result; + _context = _github.CreateEnterpriseTeamContext(EnterpriseHelper.Organization, newTeam).Result; } [GitHubEnterpriseTest] diff --git a/Octokit.Tests.Integration/Reactive/ObservableUserAdministrationClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableUserAdministrationClientTests.cs new file mode 100644 index 0000000000..efaea9a0bd --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableUserAdministrationClientTests.cs @@ -0,0 +1,201 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +namespace Octokit.Tests.Integration.Clients +{ + public class ObservableUserAdministrationClientTests + { + readonly IObservableGitHubClient _github; + + public ObservableUserAdministrationClientTests() + { + _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); + } + + private NewUser GenerateNewUserDetails() + { + string username = Helper.MakeNameWithTimestamp("user"); + string email = string.Concat(username, "@company.com"); + return new NewUser(username, email); + } + + [GitHubEnterpriseTest] + public async Task CanCreateAndDelete() + { + User checkUser = null; + + // Create a new user + var newUser = GenerateNewUserDetails(); + + var observable = _github.User.Administration.Create(newUser); + var user = await observable; + + // Check returned object (cant check email as it isn't public) + Assert.NotNull(user); + Assert.Equal(user.Login, newUser.Login); + + // Get user to check they exist + checkUser = await _github.User.Get(newUser.Login); + Assert.Equal(checkUser.Login, newUser.Login); + + // Delete the user + await _github.User.Administration.Delete(newUser.Login); + + // Ensure user doesnt exist + try + { + checkUser = await _github.User.Get(newUser.Login); + if (checkUser != null) + { + throw new Exception("User still exists!"); + } + } + catch (ApiException e) + { + if (e.StatusCode != System.Net.HttpStatusCode.NotFound) + { + throw; + } + } + } + + [GitHubEnterpriseTest] + public async Task CanRename() + { + string renamedUsername = Helper.MakeNameWithTimestamp("user-renamed"); + // Create a disposable user for the test + using (var context = _github.CreateEnterpriseUserContext(GenerateNewUserDetails()).Result) + { + var observable = _github.User.Administration.Rename( + context.UserLogin, + new UserRename(renamedUsername)); + var response = await observable; + + Assert.NotNull(response); + Assert.StartsWith("Job queued to rename user", response.Message); + Assert.EndsWith(context.UserId.ToString(), response.Url); + } + + // Remove user if it was already renamed + EnterpriseHelper.DeleteUser(renamedUsername); + } + + [GitHubEnterpriseTest] + public async Task CanAddAndDeleteImpersonationToken() + { + // Create a disposable user for the test + using (var context = _github.CreateEnterpriseUserContext(GenerateNewUserDetails()).Result) + { + // Create Impersonation token + var observable = _github.User.Administration.CreateImpersonationToken( + context.UserLogin, + new NewImpersonationToken(new string[] { "public_repo" })); + var token = await observable; + + Assert.NotNull(token); + Assert.True( + token.Scopes.Count() == 1 && + token.Scopes.All(s => s == "public_repo")); + + // Delete Impersonation token + await _github.User.Administration.DeleteImpersonationToken(context.UserLogin); + } + } + + [GitHubEnterpriseTest] + public async Task CanPromoteAndDemote() + { + User checkUser = null; + + // Create a disposable user for the test + using (var context = _github.CreateEnterpriseUserContext(GenerateNewUserDetails()).Result) + { + // Ensure user is not site admin + checkUser = await _github.User.Get(context.UserLogin); + Assert.False(checkUser.SiteAdmin); + + // Promote to site admin + await _github.User.Administration.Promote(context.UserLogin); + + // Ensure user is site admin + checkUser = await _github.User.Get(context.UserLogin); + Assert.True(checkUser.SiteAdmin); + + // Demote user + await _github.User.Administration.Demote(context.UserLogin); + + // Ensure user is not site admin + checkUser = await _github.User.Get(context.UserLogin); + Assert.False(checkUser.SiteAdmin); + } + } + + [GitHubEnterpriseTest] + public async Task CanSuspendAndUnsuspend() + { + User checkUser = null; + + // Create a disposable user for the test + using (var context = _github.CreateEnterpriseUserContext(GenerateNewUserDetails()).Result) + { + // Ensure user is not suspended + checkUser = await _github.User.Get(context.UserLogin); + Assert.False(checkUser.Suspended); + + // Suspend user + await _github.User.Administration.Suspend(context.UserLogin); + + // Ensure user is suspended + checkUser = await _github.User.Get(context.UserLogin); + Assert.True(checkUser.Suspended); + + // Unsuspend user + await _github.User.Administration.Unsuspend(context.UserLogin); + + // Ensure user is not suspended + checkUser = await _github.User.Get(context.UserLogin); + Assert.False(checkUser.Suspended); + } + } + + [GitHubEnterpriseTest(Skip = "Currently no way to add keys, so cant test listing keys")] + public async Task CanListAllPublicKeys() + { + // Create a disposable user for the test + using (var context = _github.CreateEnterpriseUserContext(GenerateNewUserDetails()).Result) + { + // Ensure user has a key + //var key = await _github.User.Keys.Create(new NewPublicKey("title", "key")); + + // Get public keys + var observable = _github.User.Administration.ListAllPublicKeys(); + var keys = await (observable.ToList()); + + Assert.NotNull(keys); + Assert.True(keys.Count > 0); + + // Delete key + //await _github.User.Administration.DeletePublicKey(key.Id); + } + } + + [GitHubEnterpriseTest(Skip = "Currently no way to add keys, so cant test deleting keys")] + public async Task CanDeletePublicKey() + { + // Create a disposable user for the test + using (var context = _github.CreateEnterpriseUserContext(GenerateNewUserDetails()).Result) + { + // Ensure user has a key + //var key = await _github.User.Keys.Create(new NewPublicKey("title", "key")); + + // Delete key + //await _github.User.Administration.DeletePublicKey(key.Id); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs b/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs index d605e71843..d247ddb30f 100644 --- a/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using NSubstitute; using Octokit.Reactive; using Xunit; @@ -7,10 +8,75 @@ namespace Octokit.Tests.Reactive { public class ObservableUserAdministrationClientTests { + public class TheCreateMethod + { + [Fact] + public void CallsIntoClient() + { + var gitHubClient = Substitute.For(); + var client = new ObservableUserAdministrationClient(gitHubClient); + + client.Create(new NewUser("auser", "email@company.com")); + + gitHubClient.User.Administration.Received().Create( + Arg.Is(a => + a.Login == "auser" && + a.Email == "email@company.com")); + } + } + + public class TheRenameMethod + { + [Fact] + public void CallsIntoClient() + { + var gitHubClient = Substitute.For(); + var client = new ObservableUserAdministrationClient(gitHubClient); + + client.Rename("auser", new UserRename("renamed-user")); + + gitHubClient.User.Administration.Received().Rename( + "auser", + Arg.Is(a => + a.Login == "renamed-user")); + } + } + + public class TheCreateImpersonationTokenMethod + { + [Fact] + public void CallsIntoClient() + { + var gitHubClient = Substitute.For(); + var client = new ObservableUserAdministrationClient(gitHubClient); + + client.CreateImpersonationToken("auser", new NewImpersonationToken(new string[] { "public_repo" })); + + gitHubClient.User.Administration.Received().CreateImpersonationToken( + "auser", + Arg.Is(a => + a.Scopes.ToList()[0] == "public_repo")); + } + } + + public class TheDeleteImpersonationTokenMethod + { + [Fact] + public void CallsIntoClient() + { + var gitHubClient = Substitute.For(); + var client = new ObservableUserAdministrationClient(gitHubClient); + + client.DeleteImpersonationToken("auser"); + + gitHubClient.User.Administration.Received().DeleteImpersonationToken("auser"); + } + } + public class ThePromoteMethod { [Fact] - public void GetsFromClientPromtePromote() + public void CallsIntoClient() { var gitHubClient = Substitute.For(); var client = new ObservableUserAdministrationClient(gitHubClient); @@ -24,7 +90,7 @@ public void GetsFromClientPromtePromote() public class TheDemoteMethod { [Fact] - public void GetsFromClientDemoteDemote() + public void CallsIntoClient() { var gitHubClient = Substitute.For(); var client = new ObservableUserAdministrationClient(gitHubClient); @@ -38,7 +104,7 @@ public void GetsFromClientDemoteDemote() public class TheSuspendMethod { [Fact] - public void GetsFromClientSuspendSuspend() + public void CallsIntoClient() { var gitHubClient = Substitute.For(); var client = new ObservableUserAdministrationClient(gitHubClient); @@ -52,7 +118,7 @@ public void GetsFromClientSuspendSuspend() public class TheUnsuspendMethod { [Fact] - public void GetsFromClientUnsuspendUnsuspend() + public void CallsIntoClient() { var gitHubClient = Substitute.For(); var client = new ObservableUserAdministrationClient(gitHubClient); @@ -63,6 +129,54 @@ public void GetsFromClientUnsuspendUnsuspend() } } + public class TheListAllPublicKeysMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableUserAdministrationClient(gitHubClient); + + var expectedUri = "admin/keys"; + + client.ListAllPublicKeys(); + + gitHubClient.Connection.Received().Get>( + Arg.Is(a => + a.ToString() == expectedUri), + null, + null); + } + } + + public class TheDeleteMethod + { + [Fact] + public void CallsIntoClient() + { + var gitHubClient = Substitute.For(); + var client = new ObservableUserAdministrationClient(gitHubClient); + + client.Delete("auser"); + + gitHubClient.User.Administration.Received().Delete("auser"); + } + } + + public class TheDeletePublicKeyMethod + { + [Fact] + public void CallsIntoClient() + { + var gitHubClient = Substitute.For(); + var client = new ObservableUserAdministrationClient(gitHubClient); + + client.DeletePublicKey(1); + + gitHubClient.User.Administration.Received().DeletePublicKey(1); + } + } + public class TheCtor { [Fact]