From 6d8e7dcd2208c497ccda24ed64708f8944388a4a Mon Sep 17 00:00:00 2001 From: Mordechai Zuber Date: Thu, 11 Feb 2016 01:37:03 +0200 Subject: [PATCH 01/18] Add extension to create a branch --- .../Helpers/ReferenceExtensionsTests.cs | 32 +++++++++++++++++++ .../Octokit.Tests.Integration.csproj | 1 + Octokit/Octokit-Mono.csproj | 1 + Octokit/Octokit-MonoAndroid.csproj | 1 + Octokit/Octokit-Monotouch.csproj | 1 + Octokit/Octokit-Portable.csproj | 1 + Octokit/Octokit-netcore45.csproj | 1 + Octokit/Octokit.csproj | 3 +- 8 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs diff --git a/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs b/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs new file mode 100644 index 00000000..495759e9 --- /dev/null +++ b/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs @@ -0,0 +1,32 @@ +using Octokit.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Integration.Helpers +{ + public class ReferenceExtensionsTests + { + [IntegrationTest] + public async Task CreateABranch() + { + var client = Helper.GetAuthenticatedClient(); + var fixture = client.Git.Reference; + + using (var context = await client.CreateRepositoryContext("public-repo")) + { + var branchFromMaster = await fixture.CreateBranch(context.RepositoryOwner, context.RepositoryName, "patch-1"); + + var branchFromPath = await fixture.CreateBranch(context.RepositoryOwner, context.RepositoryName, "patch-2", branchFromMaster); + + var allBrancheNames = (await client.Repository.GetAllBranches(context.RepositoryOwner, context.RepositoryName)).Select(b => b.Name); + + Assert.Contains("patch-1", allBrancheNames); + Assert.Contains("patch-2", allBrancheNames); + } + } + } +} diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 3cbb9462..c59e75bf 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -112,6 +112,7 @@ + diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 08bf819f..96880deb 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -437,6 +437,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 157b5ad2..2dd887d2 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -444,6 +444,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index d0926bc5..19c78573 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -440,6 +440,7 @@ + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index b110b9f9..6754306c 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -434,6 +434,7 @@ + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 0b60e716..b8caa35a 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -441,6 +441,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index ce24c522..a92a2b47 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -97,6 +97,7 @@ + @@ -473,4 +474,4 @@ --> - + \ No newline at end of file From 0b9378f5b5432ea86c2ecad029c2abe4457fdf36 Mon Sep 17 00:00:00 2001 From: Mordechai Zuber Date: Thu, 11 Feb 2016 01:38:25 +0200 Subject: [PATCH 02/18] Missing files galore --- Octokit/Helpers/ReferenceExtensions.cs | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Octokit/Helpers/ReferenceExtensions.cs diff --git a/Octokit/Helpers/ReferenceExtensions.cs b/Octokit/Helpers/ReferenceExtensions.cs new file mode 100644 index 00000000..87ffddc6 --- /dev/null +++ b/Octokit/Helpers/ReferenceExtensions.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; + +namespace Octokit.Helpers +{ + /// + /// Represents operations to simplify workink with references + /// + public static class ReferenceExtensions + { + /// + /// Creates a branch, based off the branch specified. + /// + /// The this method extends + /// The owner of the repository. + /// The name of the repository. + /// The new branch name + /// The to base the branch from + public static async Task CreateBranch(this IReferencesClient referencesClient, string owner, string name, string branchName, Reference baseReference) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(branchName, "branchName"); + Ensure.ArgumentNotNull(baseReference, "baseReference"); + + return await referencesClient.Create(owner, name, new NewReference("refs/heads/" + branchName, baseReference.Object.Sha)); + } + + /// + /// Creates a branch, based off the master branch. + /// + /// The this method extends + /// The owner of the repository. + /// The name of the repository. + /// The new branch name + public static async Task CreateBranch(this IReferencesClient referencesClient, string owner, string name, string branchName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(branchName, "branchName"); + + var baseBranch = await referencesClient.Get(owner, name, "heads/master"); + return await referencesClient.Create(owner, name, new NewReference("refs/heads/" + branchName, baseBranch.Object.Sha)); + } + } +} From 8c39e61c7aea677c625522258ca8f60de555f2eb Mon Sep 17 00:00:00 2001 From: Mordechai Zuber Date: Thu, 11 Feb 2016 07:22:56 +0200 Subject: [PATCH 03/18] Fix spelling --- Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs b/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs index 495759e9..2c82e24e 100644 --- a/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs +++ b/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs @@ -22,7 +22,7 @@ namespace Octokit.Tests.Integration.Helpers var branchFromPath = await fixture.CreateBranch(context.RepositoryOwner, context.RepositoryName, "patch-2", branchFromMaster); - var allBrancheNames = (await client.Repository.GetAllBranches(context.RepositoryOwner, context.RepositoryName)).Select(b => b.Name); + var allBranchNames = (await client.Repository.GetAllBranches(context.RepositoryOwner, context.RepositoryName)).Select(b => b.Name); Assert.Contains("patch-1", allBrancheNames); Assert.Contains("patch-2", allBrancheNames); From c0f63ff1cea63010813e894779aa3d960164df12 Mon Sep 17 00:00:00 2001 From: Mordechai Zuber Date: Thu, 11 Feb 2016 07:23:36 +0200 Subject: [PATCH 04/18] Fix spelling --- Octokit/Helpers/ReferenceExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Octokit/Helpers/ReferenceExtensions.cs b/Octokit/Helpers/ReferenceExtensions.cs index 87ffddc6..c3bcc165 100644 --- a/Octokit/Helpers/ReferenceExtensions.cs +++ b/Octokit/Helpers/ReferenceExtensions.cs @@ -3,7 +3,7 @@ namespace Octokit.Helpers { /// - /// Represents operations to simplify workink with references + /// Represents operations to simplify working with references /// public static class ReferenceExtensions { From 29a9fca8e8b9ac1aa4ef8ed68489aaed2b684220 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sat, 6 Feb 2016 22:02:48 +1000 Subject: [PATCH 05/18] Implement Enterprise LDAP Client --- .../Enterprise/EnterpriseLdapClientTests.cs | 76 ++++++++++ .../Octokit.Tests.Integration.csproj | 1 + .../Enterprise/EnterpriseLdapClientTests.cs | 136 ++++++++++++++++++ Octokit.Tests/Octokit.Tests.csproj | 1 + .../Clients/Enterprise/EnterpriseClient.cs | 9 ++ .../Enterprise/EnterpriseLdapClient.cs | 91 ++++++++++++ .../Clients/Enterprise/IEnterpriseClient.cs | 8 ++ .../Enterprise/IEnterpriseLdapClient.cs | 55 +++++++ Octokit/Helpers/ApiUrls.cs | 20 +++ .../Request/Enterprise/NewLdapMapping.cs | 36 +++++ .../Response/Enterprise/LdapSyncResponse.cs | 33 +++++ .../Models/Response/Enterprise/LdapTeam.cs | 27 ++++ .../Models/Response/Enterprise/LdapUser.cs | 27 ++++ Octokit/Octokit-Mono.csproj | 6 + Octokit/Octokit-MonoAndroid.csproj | 6 + Octokit/Octokit-Monotouch.csproj | 6 + Octokit/Octokit-Portable.csproj | 6 + Octokit/Octokit-netcore45.csproj | 6 + Octokit/Octokit.csproj | 6 + 19 files changed, 556 insertions(+) create mode 100644 Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs create mode 100644 Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs create mode 100644 Octokit/Clients/Enterprise/EnterpriseLdapClient.cs create mode 100644 Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs create mode 100644 Octokit/Models/Request/Enterprise/NewLdapMapping.cs create mode 100644 Octokit/Models/Response/Enterprise/LdapSyncResponse.cs create mode 100644 Octokit/Models/Response/Enterprise/LdapTeam.cs create mode 100644 Octokit/Models/Response/Enterprise/LdapUser.cs diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs new file mode 100644 index 00000000..38923861 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -0,0 +1,76 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Xunit; + +public class EnterpriseLdapClientTests +{ + readonly IGitHubClient _github; + readonly Team _context; + readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; + readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + + public EnterpriseLdapClientTests() + { + _github = EnterpriseHelper.GetAuthenticatedClient(); + + NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; + _context = _github.Organization.Team.Create(EnterpriseHelper.Organization, newTeam).Result; + } + + [GitHubEnterpriseTest] + public async Task CanUpdateUserMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameUser); + var ldapUser = await + _github.Enterprise.Ldap.UpdateUserMapping(EnterpriseHelper.UserName, newLDAPMapping); + + Assert.NotNull(ldapUser); + + // Get user and check mapping was updated + var checkUser = await _github.User.Get(EnterpriseHelper.UserName); + Assert.Equal(checkUser.Login, ldapUser.Login); + //Assert.Equal(checkUser.LdapDN, _distinguishedNameUser); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncUserMapping() + { + var response = await + _github.Enterprise.Ldap.QueueSyncUserMapping(EnterpriseHelper.UserName); + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + } + + [GitHubEnterpriseTest] + public async Task CanUpdateTeamMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameTeam); + var ldapTeam = await + _github.Enterprise.Ldap.UpdateTeamMapping(_context.Id, newLDAPMapping); + + Assert.NotNull(ldapTeam); + + // Get Team and check mapping was updated + var checkTeam = await _github.Organization.Team.Get(_context.Id); + Assert.Equal(checkTeam.Name, ldapTeam.Name); + //Assert.Equal(checkTeam.LDAPDN, _fixtureDistinguishedNameTeam); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncTeamMapping() + { + var response = await + _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.Id); + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + } +} diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index d8df0040..dc3278f1 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -77,6 +77,7 @@ + diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs new file mode 100644 index 00000000..430124be --- /dev/null +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class EnterpriseLdapClientTests + { + public class TheUpdateUserMappingMethod + { + readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; + + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/users/test-user/mapping"; + client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedNameUser)); + + connection.Received().Patch(Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedNameUser)); + + connection.Received().Patch( + Arg.Any(), + Arg.Is(a => + a.LdapDN == _distinguishedNameUser)); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + await Assert.ThrowsAsync(() => client.UpdateUserMapping(null, new NewLdapMapping(_distinguishedNameUser))); + await Assert.ThrowsAsync(() => client.UpdateUserMapping("test-user", null)); + } + } + + public class TheQueueSyncUserMappingMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/users/test-user/sync"; + client.QueueSyncUserMapping("test-user"); + + connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri), + Arg.Any()); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + await Assert.ThrowsAsync(() => client.QueueSyncUserMapping(null)); + } + } + + public class TheUpdateTeamMappingMethod + { + readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/teams/1/mapping"; + client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedNameTeam)); + + connection.Received().Patch( + Arg.Is(u => u.ToString() == expectedUri), + Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedNameTeam)); + + connection.Received().Patch( + Arg.Any(), + Arg.Is(a => + a.LdapDN == _distinguishedNameTeam)); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + await Assert.ThrowsAsync(() => client.UpdateTeamMapping(1, null)); + } + } + + public class TheQueueSyncTeamMappingMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/teams/1/sync"; + client.QueueSyncTeamMapping(1); + + connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri), + Arg.Any()); + } + } + } +} diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index bc124880..10d15310 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -86,6 +86,7 @@ + diff --git a/Octokit/Clients/Enterprise/EnterpriseClient.cs b/Octokit/Clients/Enterprise/EnterpriseClient.cs index 614d9bce..aa5bb9d4 100644 --- a/Octokit/Clients/Enterprise/EnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseClient.cs @@ -15,6 +15,7 @@ public EnterpriseClient(IApiConnection apiConnection) : base(apiConnection) { AdminStats = new EnterpriseAdminStatsClient(apiConnection); + Ldap = new EnterpriseLdapClient(apiConnection); License = new EnterpriseLicenseClient(apiConnection); Organization = new EnterpriseOrganizationClient(apiConnection); SearchIndexing = new EnterpriseSearchIndexingClient(apiConnection); @@ -28,6 +29,14 @@ /// public IEnterpriseAdminStatsClient AdminStats { get; private set; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public IEnterpriseLdapClient Ldap { get; private set; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs new file mode 100644 index 00000000..3fecb6ec --- /dev/null +++ b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs @@ -0,0 +1,91 @@ +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public class EnterpriseLdapClient : ApiClient, IEnterpriseLdapClient + { + public EnterpriseLdapClient(IApiConnection apiConnection) + : base(apiConnection) + { } + + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + public async Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) + { + Ensure.ArgumentNotNull(userName, "userName"); + Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); + + var endpoint = ApiUrls.EnterpriseLdapUserMapping(userName); + + return await ApiConnection.Patch(endpoint, newLdapMapping); + } + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The username to sync LDAP mapping + /// The to the queue request. + public async Task QueueSyncUserMapping(string userName) + { + Ensure.ArgumentNotNull(userName, "userName"); + + var endpoint = ApiUrls.EnterpriseLdapUserSync(userName); + + var response = await (Task)ApiConnection.Post(endpoint); + return new LdapSyncResponse(); + } + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + public async Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) + { + Ensure.ArgumentNotNull(teamId, "teamId"); + Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); + + var endpoint = ApiUrls.EnterpriseLdapTeamMapping(teamId); + + return await ApiConnection.Patch(endpoint, newLdapMapping); + } + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The to the queue request. + public async Task QueueSyncTeamMapping(int teamId) + { + Ensure.ArgumentNotNull(teamId, "teamId"); + + var endpoint = ApiUrls.EnterpriseLdapTeamSync(teamId); + + return await ApiConnection.Post(endpoint, new object()); + } + } +} diff --git a/Octokit/Clients/Enterprise/IEnterpriseClient.cs b/Octokit/Clients/Enterprise/IEnterpriseClient.cs index 899751c0..28a5db22 100644 --- a/Octokit/Clients/Enterprise/IEnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/IEnterpriseClient.cs @@ -16,6 +16,14 @@ /// IEnterpriseAdminStatsClient AdminStats { get; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + IEnterpriseLdapClient Ldap { get; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs new file mode 100644 index 00000000..a2b28063 --- /dev/null +++ b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs @@ -0,0 +1,55 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public interface IEnterpriseLdapClient + { + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The username to sync LDAP mapping + /// The to the queue request. + Task QueueSyncUserMapping(string userName); + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The to the queue request. + Task QueueSyncTeamMapping(int teamId); + } +} diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 30436b85..2c7e131c 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -1664,6 +1664,26 @@ namespace Octokit return EnterpriseAdminStats("all"); } + public static Uri EnterpriseLdapTeamMapping(int teamId) + { + return "admin/ldap/teams/{0}/mapping".FormatUri(teamId); + } + + public static Uri EnterpriseLdapTeamSync(int teamId) + { + return "admin/ldap/teams/{0}/sync".FormatUri(teamId); + } + + public static Uri EnterpriseLdapUserMapping(string userName) + { + return "admin/ldap/users/{0}/mapping".FormatUri(userName); + } + + public static Uri EnterpriseLdapUserSync(string userName) + { + return "admin/ldap/users/{0}/sync".FormatUri(userName); + } + public static Uri EnterpriseLicense() { return "enterprise/settings/license".FormatUri(); diff --git a/Octokit/Models/Request/Enterprise/NewLdapMapping.cs b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs new file mode 100644 index 00000000..b12e7843 --- /dev/null +++ b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Describes a new organization to create via the method. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewLdapMapping + { + /// + /// Initializes a new instance of the class. + /// + /// The LDAP Distinguished Name + public NewLdapMapping(string ldapDN) + { + LdapDN = ldapDN; + } + + /// + /// The LDAP Distinguished Name (required) + /// + public string LdapDN { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "LdapDn: {0}", LdapDN); + } + } + } +} diff --git a/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs new file mode 100644 index 00000000..a95391cd --- /dev/null +++ b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class LdapSyncResponse + { + public LdapSyncResponse() { } + + public LdapSyncResponse(IReadOnlyList status) + { + Status = status; + } + + public IReadOnlyList Status + { + get; + private set; + } + + internal string DebuggerDisplay + { + get + { + return String.Format(CultureInfo.InvariantCulture, "Status: {0}", string.Join("\r\n", Status)); + } + } + } +} \ No newline at end of file diff --git a/Octokit/Models/Response/Enterprise/LdapTeam.cs b/Octokit/Models/Response/Enterprise/LdapTeam.cs new file mode 100644 index 00000000..1a1ce545 --- /dev/null +++ b/Octokit/Models/Response/Enterprise/LdapTeam.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class LdapTeam : Team + { + public LdapTeam() { } + + public LdapTeam(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization, string ldapDN) + : base(url, id, name, permission, membersCount, reposCount, organization) + { + LdapDN = ldapDN; + } + + public string LdapDN { get; protected set; } + + internal new string DebuggerDisplay + { + get { return base.DebuggerDisplay; } + } + } +} diff --git a/Octokit/Models/Response/Enterprise/LdapUser.cs b/Octokit/Models/Response/Enterprise/LdapUser.cs new file mode 100644 index 00000000..e34634d8 --- /dev/null +++ b/Octokit/Models/Response/Enterprise/LdapUser.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class LdapUser : User + { + public LdapUser() { } + + public LdapUser(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDN) + : base(avatarUrl, bio, blog, collaborators, company, createdAt, diskUsage, email, followers, following, hireable, htmlUrl, totalPrivateRepos, id, location, login, name, ownedPrivateRepos, plan, privateGists, publicGists, publicRepos, url, siteAdmin) + { + LdapDN = ldapDN; + } + + public string LdapDN { get; protected set; } + + internal new string DebuggerDisplay + { + get { return base.DebuggerDisplay; } + } + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index cf4adab8..ce489ea1 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -447,6 +447,12 @@ + + + + + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index ca3641e9..dd4855fd 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -455,6 +455,12 @@ + + + + + + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 9b911c87..3f16c64c 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -451,6 +451,12 @@ + + + + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 54301735..1a2b7205 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -444,6 +444,12 @@ + + + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 47276432..ecc416fe 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -451,6 +451,12 @@ + + + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index d293cab7..96a611e3 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -59,10 +59,12 @@ + + @@ -110,6 +112,7 @@ + @@ -150,6 +153,9 @@ + + + From 6965ac44b24a4cb8607b43830ad65a5359fdbabe Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sat, 6 Feb 2016 22:03:09 +1000 Subject: [PATCH 06/18] Implement Reactive version --- .../Enterprise/IObservableEnterpriseClient.cs | 8 ++ .../IObservableEnterpriseLdapClient.cs | 57 +++++++++++++ .../Enterprise/ObservableEnterpriseClient.cs | 9 ++ .../ObservableEnterpriseLdapClient.cs | 79 +++++++++++++++++ Octokit.Reactive/Octokit.Reactive-Mono.csproj | 2 + .../Octokit.Reactive-MonoAndroid.csproj | 2 + .../Octokit.Reactive-Monotouch.csproj | 2 + Octokit.Reactive/Octokit.Reactive.csproj | 2 + .../Octokit.Tests.Integration.csproj | 1 + .../ObservableEnterpriseLdapClientTests.cs | 84 +++++++++++++++++++ Octokit.Tests/Octokit.Tests.csproj | 1 + .../ObservableEnterpriseLdapClientTests.cs | 74 ++++++++++++++++ 12 files changed, 321 insertions(+) create mode 100644 Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs create mode 100644 Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs create mode 100644 Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs create mode 100644 Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs index 74186a8f..018c48e8 100644 --- a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs @@ -16,6 +16,14 @@ /// IObservableEnterpriseAdminStatsClient AdminStats { get; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + IObservableEnterpriseLdapClient Ldap { get; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs new file mode 100644 index 00000000..da76f52d --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs @@ -0,0 +1,57 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public interface IObservableEnterpriseLdapClient + { + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The username to sync LDAP mapping + /// The to the queue request. + IObservable QueueSyncUserMapping(string userName); + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The to the queue request. + IObservable QueueSyncTeamMapping(int teamId); + } +} diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs index 0aa4c920..a463212a 100644 --- a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs @@ -13,6 +13,7 @@ Ensure.ArgumentNotNull(client, "client"); AdminStats = new ObservableEnterpriseAdminStatsClient(client); + Ldap = new ObservableEnterpriseLdapClient(client); License = new ObservableEnterpriseLicenseClient(client); Organization = new ObservableEnterpriseOrganizationClient(client); SearchIndexing = new ObservableEnterpriseSearchIndexingClient(client); @@ -26,6 +27,14 @@ /// public IObservableEnterpriseAdminStatsClient AdminStats { get; private set; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public IObservableEnterpriseLdapClient Ldap { get; private set; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs new file mode 100644 index 00000000..cd76a65e --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs @@ -0,0 +1,79 @@ +using System; +using System.Reactive.Threading.Tasks; +using Octokit; + + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public class ObservableEnterpriseLdapClient : IObservableEnterpriseLdapClient + { + readonly IEnterpriseLdapClient _client; + + public ObservableEnterpriseLdapClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Enterprise.Ldap; + } + + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + public IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) + { + return _client.UpdateUserMapping(userName, newLdapMapping).ToObservable(); + } + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The username to sync LDAP mapping + /// The to the queue request. + public IObservable QueueSyncUserMapping(string userName) + { + return _client.QueueSyncUserMapping(userName).ToObservable(); + } + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + public IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) + { + return _client.UpdateTeamMapping(teamId, newLdapMapping).ToObservable(); + } + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The to the queue request. + public IObservable QueueSyncTeamMapping(int teamId) + { + return _client.QueueSyncTeamMapping(teamId).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj index 10d63457..41db9810 100644 --- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj @@ -171,6 +171,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj index d0f878da..2a01d4da 100644 --- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj +++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj @@ -179,6 +179,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj index ce3efdbe..c3855b70 100644 --- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj @@ -175,6 +175,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 607a71eb..4c69b57f 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -75,6 +75,7 @@ Properties\SolutionInfo.cs + @@ -82,6 +83,7 @@ + diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index dc3278f1..d5d09495 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -128,6 +128,7 @@ + diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs new file mode 100644 index 00000000..f21cc5ed --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -0,0 +1,84 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration +{ + public class ObservableEnterpriseLDAPClientTests + { + readonly IObservableGitHubClient _github; + readonly Team _context; + readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; + readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + + public ObservableEnterpriseLDAPClientTests() + { + _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); + + NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; + var observable = _github.Organization.Team.Create(EnterpriseHelper.Organization, newTeam); + _context = observable.Wait(); + } + + [GitHubEnterpriseTest] + public async Task CanUpdateUserMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameUser); + var observable = + _github.Enterprise.Ldap.UpdateUserMapping(EnterpriseHelper.UserName, newLDAPMapping); + var ldapUser = await observable; + + Assert.NotNull(ldapUser); + + // Get user and check mapping was updated + var checkUser = await _github.User.Get(EnterpriseHelper.UserName); + Assert.Equal(checkUser.Login, ldapUser.Login); + //Assert.Equal(checkUser.LdapDN, _distinguishedNameUser); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncUserMapping() + { + var observable = + _github.Enterprise.Ldap.QueueSyncUserMapping(EnterpriseHelper.UserName); + var response = await observable; + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + } + + [GitHubEnterpriseTest] + public async Task CanUpdateTeamMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameTeam); + var observable = + _github.Enterprise.Ldap.UpdateTeamMapping(_context.Id, newLDAPMapping); + var ldapTeam = await observable; + + Assert.NotNull(ldapTeam); + + // Get Team and check mapping was updated + var checkTeam = await _github.Organization.Team.Get(_context.Id); + Assert.Equal(checkTeam.Name, ldapTeam.Name); + //Assert.Equal(checkTeam.LdapDN, _distinguishedNameTeam); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncTeamMapping() + { + var observable = + _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.Id); + var response = await observable; + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + } + } +} \ No newline at end of file diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 10d15310..cca0f7ae 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -192,6 +192,7 @@ + diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs new file mode 100644 index 00000000..c8779945 --- /dev/null +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -0,0 +1,74 @@ +using System; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests +{ + public class ObservableEnterpriseLDAPClientTests + { + public class TheUpdateUserMappingMethod + { + readonly string _distinguishedName = "uid=test-user,ou=users,dc=company,dc=com"; + + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedName)); + github.Enterprise.Ldap.Received(1).UpdateUserMapping( + Arg.Is(a => a == "test-user"), + Arg.Is(a => + a.LdapDN == _distinguishedName)); + } + } + + public class TheQueueSyncUserMappingMethod + { + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.QueueSyncUserMapping("test-user"); + github.Enterprise.Ldap.Received(1).QueueSyncUserMapping( + Arg.Is(a => a == "test-user")); + } + } + + public class TheUpdateTeamMappingMethod + { + readonly string _distinguishedName = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedName)); + github.Enterprise.Ldap.Received(1).UpdateTeamMapping( + Arg.Is(a => a == 1), + Arg.Is(a => + a.LdapDN == _distinguishedName)); + } + } + + public class TheQueueSyncTeamMappingMethod + { + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.QueueSyncTeamMapping(1); + github.Enterprise.Ldap.Received(1).QueueSyncTeamMapping( + Arg.Is(a => a == 1)); + } + } + } +} From 1f13001fd8d20148ce682dedefdfb5cdc0fcd5f1 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sat, 6 Feb 2016 22:41:17 +1000 Subject: [PATCH 07/18] Change the QueueLdapSync calls to use Post(uri) with no body/data --- .../IObservableEnterpriseLdapClient.cs | 9 +++--- .../ObservableEnterpriseLdapClient.cs | 9 +++--- .../Enterprise/EnterpriseLdapClientTests.cs | 21 +++++++------ Octokit.Tests.Integration/EnterpriseHelper.cs | 16 ++++++++++ .../Helpers/EnterpriseTeamContext.cs | 28 +++++++++++++++++ .../Helpers/GithubClientExtensions.cs | 7 +++++ .../Octokit.Tests.Integration.csproj | 1 + .../ObservableEnterpriseLdapClientTests.cs | 31 ++++++++++--------- .../Enterprise/EnterpriseLdapClientTests.cs | 10 +++--- .../Enterprise/EnterpriseLdapClient.cs | 29 +++++++++++------ .../Enterprise/IEnterpriseLdapClient.cs | 10 +++--- Octokit/Http/ApiConnection.cs | 15 +++++++++ Octokit/Http/Connection.cs | 7 +++++ Octokit/Http/IApiConnection.cs | 9 ++++++ Octokit/Http/IConnection.cs | 9 ++++++ .../Response/Enterprise/LdapSyncResponse.cs | 2 +- Octokit/Octokit-Mono.csproj | 2 +- Octokit/Octokit-MonoAndroid.csproj | 2 +- Octokit/Octokit-Monotouch.csproj | 2 +- Octokit/Octokit-Portable.csproj | 2 +- Octokit/Octokit-netcore45.csproj | 2 +- Octokit/Octokit.csproj | 2 +- 22 files changed, 165 insertions(+), 60 deletions(-) create mode 100644 Octokit.Tests.Integration/Helpers/EnterpriseTeamContext.cs diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs index da76f52d..883980e3 100644 --- a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Reactive; using System.Reactive.Threading.Tasks; namespace Octokit.Reactive @@ -22,7 +23,7 @@ namespace Octokit.Reactive /// The /// The object. IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); - + /// /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -30,7 +31,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// /// The username to sync LDAP mapping - /// The to the queue request. + /// The of the queue request. IObservable QueueSyncUserMapping(string userName); /// @@ -43,7 +44,7 @@ namespace Octokit.Reactive /// The /// The object. IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); - + /// /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -51,7 +52,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The to the queue request. + /// The of the queue request. IObservable QueueSyncTeamMapping(int teamId); } } diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs index cd76a65e..95b5b6bc 100644 --- a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive; using System.Reactive.Threading.Tasks; using Octokit; @@ -35,7 +36,7 @@ namespace Octokit.Reactive { return _client.UpdateUserMapping(userName, newLdapMapping).ToObservable(); } - + /// /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -43,7 +44,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// /// The username to sync LDAP mapping - /// The to the queue request. + /// The of the queue request. public IObservable QueueSyncUserMapping(string userName) { return _client.QueueSyncUserMapping(userName).ToObservable(); @@ -62,7 +63,7 @@ namespace Octokit.Reactive { return _client.UpdateTeamMapping(teamId, newLdapMapping).ToObservable(); } - + /// /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -70,7 +71,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The to the queue request. + /// The of the queue request. public IObservable QueueSyncTeamMapping(int teamId) { return _client.QueueSyncTeamMapping(teamId).ToObservable(); diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs index 38923861..ff6bad79 100644 --- a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -3,23 +3,24 @@ using System.Linq; using System.Threading.Tasks; using Octokit; using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; using Xunit; public class EnterpriseLdapClientTests { readonly IGitHubClient _github; - readonly Team _context; + readonly EnterpriseTeamContext _context; readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; public EnterpriseLdapClientTests() { _github = EnterpriseHelper.GetAuthenticatedClient(); - + NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; - _context = _github.Organization.Team.Create(EnterpriseHelper.Organization, newTeam).Result; + _context = _github.CreateEnterpriseTeamContext(EnterpriseHelper.Organization, newTeam).Result; } - + [GitHubEnterpriseTest] public async Task CanUpdateUserMapping() { @@ -40,24 +41,24 @@ public class EnterpriseLdapClientTests { var response = await _github.Enterprise.Ldap.QueueSyncUserMapping(EnterpriseHelper.UserName); - + // Check response message indicates LDAP sync was queued Assert.NotNull(response); Assert.NotNull(response.Status); Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); } - + [GitHubEnterpriseTest] public async Task CanUpdateTeamMapping() { var newLDAPMapping = new NewLdapMapping(_distinguishedNameTeam); var ldapTeam = await - _github.Enterprise.Ldap.UpdateTeamMapping(_context.Id, newLDAPMapping); + _github.Enterprise.Ldap.UpdateTeamMapping(_context.TeamId, newLDAPMapping); Assert.NotNull(ldapTeam); // Get Team and check mapping was updated - var checkTeam = await _github.Organization.Team.Get(_context.Id); + var checkTeam = await _github.Organization.Team.Get(_context.TeamId); Assert.Equal(checkTeam.Name, ldapTeam.Name); //Assert.Equal(checkTeam.LDAPDN, _fixtureDistinguishedNameTeam); } @@ -66,8 +67,8 @@ public class EnterpriseLdapClientTests public async Task CanQueueSyncTeamMapping() { var response = await - _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.Id); - + _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.TeamId); + // Check response message indicates LDAP sync was queued Assert.NotNull(response); Assert.NotNull(response.Status); diff --git a/Octokit.Tests.Integration/EnterpriseHelper.cs b/Octokit.Tests.Integration/EnterpriseHelper.cs index 9a1ab54e..63323012 100644 --- a/Octokit.Tests.Integration/EnterpriseHelper.cs +++ b/Octokit.Tests.Integration/EnterpriseHelper.cs @@ -124,6 +124,22 @@ namespace Octokit.Tests.Integration catch { } } + public static void DeleteTeam(Team team) + { + if (team != null) + DeleteTeam(team.Id); + } + + public static void DeleteTeam(int teamId) + { + var api = GetAuthenticatedClient(); + try + { + api.Organization.Team.Delete(teamId).Wait(TimeSpan.FromSeconds(15)); + } + catch { } + } + public static IGitHubClient GetAuthenticatedClient() { return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl) diff --git a/Octokit.Tests.Integration/Helpers/EnterpriseTeamContext.cs b/Octokit.Tests.Integration/Helpers/EnterpriseTeamContext.cs new file mode 100644 index 00000000..3b910a2a --- /dev/null +++ b/Octokit.Tests.Integration/Helpers/EnterpriseTeamContext.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit.Tests.Integration.Helpers +{ + internal sealed class EnterpriseTeamContext : IDisposable + { + internal EnterpriseTeamContext(Team team) + { + Team = team; + TeamId = team.Id; + TeamName = team.Name; + } + + internal int TeamId { get; private set; } + internal string TeamName { get; private set; } + + internal Team Team { get; private set; } + + public void Dispose() + { + EnterpriseHelper.DeleteTeam(Team); + } + } +} diff --git a/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs b/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs index 58c53d05..badd2b56 100644 --- a/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs +++ b/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs @@ -29,5 +29,12 @@ namespace Octokit.Tests.Integration.Helpers return new RepositoryContext(repo); } + + internal async static Task CreateEnterpriseTeamContext(this IGitHubClient client, string organization, NewTeam newTeam) + { + var team = await client.Organization.Team.Create(organization, newTeam); + + return new EnterpriseTeamContext(team); + } } } \ 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 d5d09495..c5d863e8 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -118,6 +118,7 @@ + diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index f21cc5ed..99099ac9 100644 --- a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -3,24 +3,25 @@ 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 { - public class ObservableEnterpriseLDAPClientTests + public class ObservableEnterpriseLdapClientTests { readonly IObservableGitHubClient _github; - readonly Team _context; + readonly EnterpriseTeamContext _context; readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; - - public ObservableEnterpriseLDAPClientTests() + + public ObservableEnterpriseLdapClientTests() { - _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); + var gitHub = EnterpriseHelper.GetAuthenticatedClient(); + _github = new ObservableGitHubClient(gitHub); NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; - var observable = _github.Organization.Team.Create(EnterpriseHelper.Organization, newTeam); - _context = observable.Wait(); + _context = gitHub.CreateEnterpriseTeamContext(EnterpriseHelper.Organization, newTeam).Result; } [GitHubEnterpriseTest] @@ -30,7 +31,7 @@ namespace Octokit.Tests.Integration var observable = _github.Enterprise.Ldap.UpdateUserMapping(EnterpriseHelper.UserName, newLDAPMapping); var ldapUser = await observable; - + Assert.NotNull(ldapUser); // Get user and check mapping was updated @@ -51,19 +52,19 @@ namespace Octokit.Tests.Integration Assert.NotNull(response.Status); Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); } - + [GitHubEnterpriseTest] public async Task CanUpdateTeamMapping() { var newLDAPMapping = new NewLdapMapping(_distinguishedNameTeam); var observable = - _github.Enterprise.Ldap.UpdateTeamMapping(_context.Id, newLDAPMapping); + _github.Enterprise.Ldap.UpdateTeamMapping(_context.TeamId, newLDAPMapping); var ldapTeam = await observable; - + Assert.NotNull(ldapTeam); // Get Team and check mapping was updated - var checkTeam = await _github.Organization.Team.Get(_context.Id); + var checkTeam = await _github.Organization.Team.Get(_context.TeamId); Assert.Equal(checkTeam.Name, ldapTeam.Name); //Assert.Equal(checkTeam.LdapDN, _distinguishedNameTeam); } @@ -72,13 +73,13 @@ namespace Octokit.Tests.Integration public async Task CanQueueSyncTeamMapping() { var observable = - _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.Id); + _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.TeamId); var response = await observable; - + // Check response message indicates LDAP sync was queued Assert.NotNull(response); Assert.NotNull(response.Status); Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); } } -} \ No newline at end of file +} diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs index 430124be..ee30b8e6 100644 --- a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -59,9 +59,8 @@ namespace Octokit.Tests.Clients string expectedUri = "admin/ldap/users/test-user/sync"; client.QueueSyncUserMapping("test-user"); - connection.Received().Post( - Arg.Is(u => u.ToString() == expectedUri), - Arg.Any()); + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri)); } [Fact] @@ -127,9 +126,8 @@ namespace Octokit.Tests.Clients string expectedUri = "admin/ldap/teams/1/sync"; client.QueueSyncTeamMapping(1); - connection.Received().Post( - Arg.Is(u => u.ToString() == expectedUri), - Arg.Any()); + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri)); } } } diff --git a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs index 3fecb6ec..62aaff4d 100644 --- a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs @@ -33,23 +33,28 @@ namespace Octokit return await ApiConnection.Patch(endpoint, newLdapMapping); } - + /// /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). /// /// /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// - /// The username to sync LDAP mapping - /// The to the queue request. + /// The userName to sync LDAP mapping + /// The of the queue request. public async Task QueueSyncUserMapping(string userName) { Ensure.ArgumentNotNull(userName, "userName"); var endpoint = ApiUrls.EnterpriseLdapUserSync(userName); - - var response = await (Task)ApiConnection.Post(endpoint); - return new LdapSyncResponse(); + + var response = await Connection.Post(endpoint); + if (response.HttpResponse.StatusCode != HttpStatusCode.Created) + { + throw new ApiException("Invalid Status Code returned. Expected a 201", response.HttpResponse.StatusCode); + } + + return response.Body; } /// @@ -78,14 +83,20 @@ namespace Octokit /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The to the queue request. + /// The of the queue request. public async Task QueueSyncTeamMapping(int teamId) { Ensure.ArgumentNotNull(teamId, "teamId"); var endpoint = ApiUrls.EnterpriseLdapTeamSync(teamId); - - return await ApiConnection.Post(endpoint, new object()); + + var response = await Connection.Post(endpoint); + if (response.HttpResponse.StatusCode != HttpStatusCode.Created) + { + throw new ApiException("Invalid Status Code returned. Expected a 201", response.HttpResponse.StatusCode); + } + + return response.Body; } } } diff --git a/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs index a2b28063..9de08422 100644 --- a/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs +++ b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs @@ -20,7 +20,7 @@ namespace Octokit /// The /// The object. Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); - + /// /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -28,9 +28,9 @@ namespace Octokit /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// /// The username to sync LDAP mapping - /// The to the queue request. + /// The of the queue request. Task QueueSyncUserMapping(string userName); - + /// /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -41,7 +41,7 @@ namespace Octokit /// The /// The object. Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); - + /// /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -49,7 +49,7 @@ namespace Octokit /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The to the queue request. + /// The of the queue request. Task QueueSyncTeamMapping(int teamId); } } diff --git a/Octokit/Http/ApiConnection.cs b/Octokit/Http/ApiConnection.cs index 5821e327..33a77f6e 100644 --- a/Octokit/Http/ApiConnection.cs +++ b/Octokit/Http/ApiConnection.cs @@ -161,6 +161,21 @@ namespace Octokit return Connection.Post(uri); } + /// + /// Creates a new API resource in the list at the specified URI. + /// + /// The API resource's type. + /// URI of the API resource to get + /// The created API resource. + /// Thrown when an API error occurs. + public async Task Post(Uri uri) + { + Ensure.ArgumentNotNull(uri, "uri"); + + var response = await Connection.Post(uri).ConfigureAwait(false); + return response.Body; + } + /// /// Creates a new API resource in the list at the specified URI. /// diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index 071758a5..dabee271 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -237,6 +237,13 @@ namespace Octokit return response.HttpResponse.StatusCode; } + public Task> Post(Uri uri) + { + Ensure.ArgumentNotNull(uri, "uri"); + + return SendData(uri, HttpMethod.Post, null, null, null, CancellationToken.None); + } + public Task> Post(Uri uri, object body, string accepts, string contentType) { Ensure.ArgumentNotNull(uri, "uri"); diff --git a/Octokit/Http/IApiConnection.cs b/Octokit/Http/IApiConnection.cs index c111707b..5805df83 100644 --- a/Octokit/Http/IApiConnection.cs +++ b/Octokit/Http/IApiConnection.cs @@ -100,6 +100,15 @@ namespace Octokit /// Thrown when an API error occurs. Task Post(Uri uri); + /// + /// Creates a new API resource in the list at the specified URI. + /// + /// The API resource's type. + /// URI endpoint to send request to + /// The created API resource. + /// Thrown when an API error occurs. + Task Post(Uri uri); + /// /// Creates a new API resource in the list at the specified URI. /// diff --git a/Octokit/Http/IConnection.cs b/Octokit/Http/IConnection.cs index 69bdde9b..137f488e 100644 --- a/Octokit/Http/IConnection.cs +++ b/Octokit/Http/IConnection.cs @@ -105,6 +105,15 @@ namespace Octokit /// representing the received HTTP response Task Post(Uri uri); + /// + /// Performs an asynchronous HTTP POST request. + /// Attempts to map the response body to an object of type + /// + /// The type to map the response to + /// URI endpoint to send request to + /// representing the received HTTP response + Task> Post(Uri uri); + /// /// Performs an asynchronous HTTP POST request. /// Attempts to map the response body to an object of type diff --git a/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs index a95391cd..65872c4e 100644 --- a/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs +++ b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs @@ -30,4 +30,4 @@ namespace Octokit } } } -} \ No newline at end of file +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index ce489ea1..9eb6dec6 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -450,9 +450,9 @@ - + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index dd4855fd..d42ba1df 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -458,9 +458,9 @@ - + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 3f16c64c..91023b16 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -454,9 +454,9 @@ - + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 1a2b7205..bbb7bebd 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -447,9 +447,9 @@ - + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index ecc416fe..02578d49 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -454,9 +454,9 @@ - + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 96a611e3..72354fbd 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -153,9 +153,9 @@ + - From e4eb1166b7b052ab5dd4d1ad8f740b1aed5b74f7 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Mon, 8 Feb 2016 16:13:23 +1000 Subject: [PATCH 08/18] Due to api serialisation, must use LdapDn rather than LdapDN (the latter turns into ldap_d_n instead of ldap_dn) Get rid of LdapTeam and LdapUser, the regular Team and User objects contain ldap_dn field if LDAP sync is enabled --- .../IObservableEnterpriseLdapClient.cs | 12 ++++----- .../ObservableEnterpriseLdapClient.cs | 12 ++++----- .../Enterprise/EnterpriseLdapClient.cs | 18 ++++++------- .../Enterprise/IEnterpriseLdapClient.cs | 12 ++++----- .../Request/Enterprise/NewLdapMapping.cs | 13 ++++++--- .../Response/Enterprise/LdapSyncResponse.cs | 6 ++--- .../Models/Response/Enterprise/LdapTeam.cs | 27 ------------------- .../Models/Response/Enterprise/LdapUser.cs | 27 ------------------- Octokit/Models/Response/Team.cs | 13 ++++++++- Octokit/Models/Response/User.cs | 13 ++++++++- Octokit/Octokit-Mono.csproj | 2 -- Octokit/Octokit-MonoAndroid.csproj | 2 -- Octokit/Octokit-Monotouch.csproj | 2 -- Octokit/Octokit-Portable.csproj | 2 -- Octokit/Octokit-netcore45.csproj | 2 -- Octokit/Octokit.csproj | 2 -- 16 files changed, 63 insertions(+), 102 deletions(-) delete mode 100644 Octokit/Models/Response/Enterprise/LdapTeam.cs delete mode 100644 Octokit/Models/Response/Enterprise/LdapUser.cs diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs index 883980e3..d5f4bd1f 100644 --- a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs @@ -21,8 +21,8 @@ namespace Octokit.Reactive /// /// The username to update LDAP mapping /// The - /// The object. - IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); + /// The object. + IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); /// /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). @@ -31,7 +31,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// /// The username to sync LDAP mapping - /// The of the queue request. + /// The of the queue request. IObservable QueueSyncUserMapping(string userName); /// @@ -42,8 +42,8 @@ namespace Octokit.Reactive /// /// The teamId to update LDAP mapping /// The - /// The object. - IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); + /// The object. + IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); /// /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). @@ -52,7 +52,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The of the queue request. + /// The of the queue request. IObservable QueueSyncTeamMapping(int teamId); } } diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs index 95b5b6bc..f096e496 100644 --- a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs @@ -31,8 +31,8 @@ namespace Octokit.Reactive /// /// The username to update LDAP mapping /// The - /// The object. - public IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) + /// The object. + public IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) { return _client.UpdateUserMapping(userName, newLdapMapping).ToObservable(); } @@ -44,7 +44,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// /// The username to sync LDAP mapping - /// The of the queue request. + /// The of the queue request. public IObservable QueueSyncUserMapping(string userName) { return _client.QueueSyncUserMapping(userName).ToObservable(); @@ -58,8 +58,8 @@ namespace Octokit.Reactive /// /// The teamId to update LDAP mapping /// The - /// The object. - public IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) + /// The object. + public IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) { return _client.UpdateTeamMapping(teamId, newLdapMapping).ToObservable(); } @@ -71,7 +71,7 @@ namespace Octokit.Reactive /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The of the queue request. + /// The of the queue request. public IObservable QueueSyncTeamMapping(int teamId) { return _client.QueueSyncTeamMapping(teamId).ToObservable(); diff --git a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs index 62aaff4d..0073384e 100644 --- a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs @@ -23,15 +23,15 @@ namespace Octokit /// /// The username to update LDAP mapping /// The - /// The object. - public async Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) + /// The object. + public async Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) { Ensure.ArgumentNotNull(userName, "userName"); Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); var endpoint = ApiUrls.EnterpriseLdapUserMapping(userName); - return await ApiConnection.Patch(endpoint, newLdapMapping); + return await ApiConnection.Patch(endpoint, newLdapMapping); } /// @@ -41,7 +41,7 @@ namespace Octokit /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// /// The userName to sync LDAP mapping - /// The of the queue request. + /// The of the queue request. public async Task QueueSyncUserMapping(string userName) { Ensure.ArgumentNotNull(userName, "userName"); @@ -65,17 +65,17 @@ namespace Octokit /// /// The teamId to update LDAP mapping /// The - /// The object. - public async Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) + /// The object. + public async Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) { Ensure.ArgumentNotNull(teamId, "teamId"); Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); var endpoint = ApiUrls.EnterpriseLdapTeamMapping(teamId); - return await ApiConnection.Patch(endpoint, newLdapMapping); + return await ApiConnection.Patch(endpoint, newLdapMapping); } - + /// /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -83,7 +83,7 @@ namespace Octokit /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The of the queue request. + /// The of the queue request. public async Task QueueSyncTeamMapping(int teamId) { Ensure.ArgumentNotNull(teamId, "teamId"); diff --git a/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs index 9de08422..68f30cdd 100644 --- a/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs +++ b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs @@ -18,8 +18,8 @@ namespace Octokit /// /// The username to update LDAP mapping /// The - /// The object. - Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); + /// The object. + Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); /// /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). @@ -28,7 +28,7 @@ namespace Octokit /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user /// /// The username to sync LDAP mapping - /// The of the queue request. + /// The of the queue request. Task QueueSyncUserMapping(string userName); /// @@ -39,8 +39,8 @@ namespace Octokit /// /// The teamId to update LDAP mapping /// The - /// The object. - Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); + /// The object. + Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); /// /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). @@ -49,7 +49,7 @@ namespace Octokit /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team /// /// The teamId to update LDAP mapping - /// The of the queue request. + /// The of the queue request. Task QueueSyncTeamMapping(int teamId); } } diff --git a/Octokit/Models/Request/Enterprise/NewLdapMapping.cs b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs index b12e7843..f660a7a1 100644 --- a/Octokit/Models/Request/Enterprise/NewLdapMapping.cs +++ b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs @@ -1,6 +1,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace Octokit @@ -15,21 +16,25 @@ namespace Octokit /// Initializes a new instance of the class. /// /// The LDAP Distinguished Name - public NewLdapMapping(string ldapDN) + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] + public NewLdapMapping(string ldapDn) { - LdapDN = ldapDN; + LdapDn = ldapDn; } /// /// The LDAP Distinguished Name (required) /// - public string LdapDN { get; private set; } + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] + public string LdapDn { get; private set; } internal string DebuggerDisplay { get { - return string.Format(CultureInfo.InvariantCulture, "LdapDn: {0}", LdapDN); + return string.Format(CultureInfo.InvariantCulture, "LdapDn: {0}", LdapDn); } } } diff --git a/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs index 65872c4e..2ed74f9a 100644 --- a/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs +++ b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs @@ -11,12 +11,12 @@ namespace Octokit { public LdapSyncResponse() { } - public LdapSyncResponse(IReadOnlyList status) + public LdapSyncResponse(string status) { Status = status; } - public IReadOnlyList Status + public string Status { get; private set; @@ -26,7 +26,7 @@ namespace Octokit { get { - return String.Format(CultureInfo.InvariantCulture, "Status: {0}", string.Join("\r\n", Status)); + return String.Format(CultureInfo.InvariantCulture, "Status: {0}", Status); } } } diff --git a/Octokit/Models/Response/Enterprise/LdapTeam.cs b/Octokit/Models/Response/Enterprise/LdapTeam.cs deleted file mode 100644 index 1a1ce545..00000000 --- a/Octokit/Models/Response/Enterprise/LdapTeam.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; - -namespace Octokit -{ - [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class LdapTeam : Team - { - public LdapTeam() { } - - public LdapTeam(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization, string ldapDN) - : base(url, id, name, permission, membersCount, reposCount, organization) - { - LdapDN = ldapDN; - } - - public string LdapDN { get; protected set; } - - internal new string DebuggerDisplay - { - get { return base.DebuggerDisplay; } - } - } -} diff --git a/Octokit/Models/Response/Enterprise/LdapUser.cs b/Octokit/Models/Response/Enterprise/LdapUser.cs deleted file mode 100644 index e34634d8..00000000 --- a/Octokit/Models/Response/Enterprise/LdapUser.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; - -namespace Octokit -{ - [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class LdapUser : User - { - public LdapUser() { } - - public LdapUser(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDN) - : base(avatarUrl, bio, blog, collaborators, company, createdAt, diskUsage, email, followers, following, hireable, htmlUrl, totalPrivateRepos, id, location, login, name, ownedPrivateRepos, plan, privateGists, publicGists, publicRepos, url, siteAdmin) - { - LdapDN = ldapDN; - } - - public string LdapDN { get; protected set; } - - internal new string DebuggerDisplay - { - get { return base.DebuggerDisplay; } - } - } -} diff --git a/Octokit/Models/Response/Team.cs b/Octokit/Models/Response/Team.cs index 314c1b0a..60f54946 100644 --- a/Octokit/Models/Response/Team.cs +++ b/Octokit/Models/Response/Team.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace Octokit @@ -12,7 +13,9 @@ namespace Octokit { public Team() { } - public Team(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization) + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Dn")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] + public Team(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization, string ldapDn) { Url = url; Id = id; @@ -21,6 +24,7 @@ namespace Octokit MembersCount = membersCount; ReposCount = reposCount; Organization = organization; + LdapDn = ldapDn; } /// @@ -58,6 +62,13 @@ namespace Octokit /// public Organization Organization { get; protected set; } + /// + /// LDAP Binding (GitHub Enterprise only) + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] + public string LdapDn { get; protected set; } + internal string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "Name: {0} ", Name); } diff --git a/Octokit/Models/Response/User.cs b/Octokit/Models/Response/User.cs index 31b3d40a..4eb745ae 100644 --- a/Octokit/Models/Response/User.cs +++ b/Octokit/Models/Response/User.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace Octokit @@ -12,10 +13,13 @@ namespace Octokit { public User() { } - public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin) + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] + public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDn) : base(avatarUrl, bio, blog, collaborators, company, createdAt, diskUsage, email, followers, following, hireable, htmlUrl, totalPrivateRepos, id, location, login, name, ownedPrivateRepos, plan, privateGists, publicGists, publicRepos, AccountType.User, url) { SiteAdmin = siteAdmin; + LdapDn = ldapDn; } /// @@ -23,6 +27,13 @@ namespace Octokit /// public bool SiteAdmin { get; protected set; } + /// + /// LDAP Binding (GitHub Enterprise only) + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] + public string LdapDn { get; protected set; } + internal string DebuggerDisplay { get diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 9eb6dec6..34776289 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -450,8 +450,6 @@ - - diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index d42ba1df..f57c2adf 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -458,8 +458,6 @@ - - diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 91023b16..fab21afa 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -454,8 +454,6 @@ - - diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index bbb7bebd..93d40338 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -447,8 +447,6 @@ - - diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 02578d49..00a65ff9 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -454,8 +454,6 @@ - - diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 72354fbd..7616a418 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -154,8 +154,6 @@ - - From 8be8c41966b57de0d994120e733ff02c9d111438 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Mon, 8 Feb 2016 16:18:46 +1000 Subject: [PATCH 09/18] Rework tests. Note that test user and distinguished names have been replaced with generic values as we cant reveal our own domain details --- .../Enterprise/EnterpriseLdapClientTests.cs | 32 +++++++++++------ .../ObservableEnterpriseLdapClientTests.cs | 34 +++++++++++++------ .../Enterprise/EnterpriseLdapClientTests.cs | 12 +++---- .../ObservableEnterpriseLdapClientTests.cs | 6 ++-- 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs index ff6bad79..da837b17 100644 --- a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -6,12 +6,15 @@ using Octokit.Tests.Integration; using Octokit.Tests.Integration.Helpers; using Xunit; -public class EnterpriseLdapClientTests +public class EnterpriseLdapClientTests : IDisposable { readonly IGitHubClient _github; - readonly EnterpriseTeamContext _context; + + readonly string _testUser = "test-user"; readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; - readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + + readonly EnterpriseTeamContext _context; + readonly string _distinguishedNameTeam = "cn=test-team,ou=groups,dc=company,dc=com"; public EnterpriseLdapClientTests() { @@ -26,26 +29,28 @@ public class EnterpriseLdapClientTests { var newLDAPMapping = new NewLdapMapping(_distinguishedNameUser); var ldapUser = await - _github.Enterprise.Ldap.UpdateUserMapping(EnterpriseHelper.UserName, newLDAPMapping); + _github.Enterprise.Ldap.UpdateUserMapping(_testUser, newLDAPMapping); Assert.NotNull(ldapUser); + Assert.NotNull(ldapUser.LdapDn); + Assert.Equal(ldapUser.LdapDn, _distinguishedNameUser); // Get user and check mapping was updated - var checkUser = await _github.User.Get(EnterpriseHelper.UserName); + var checkUser = await _github.User.Get(_testUser); Assert.Equal(checkUser.Login, ldapUser.Login); - //Assert.Equal(checkUser.LdapDN, _distinguishedNameUser); + Assert.Equal(checkUser.LdapDn, _distinguishedNameUser); } [GitHubEnterpriseTest] public async Task CanQueueSyncUserMapping() { var response = await - _github.Enterprise.Ldap.QueueSyncUserMapping(EnterpriseHelper.UserName); + _github.Enterprise.Ldap.QueueSyncUserMapping(_testUser); // Check response message indicates LDAP sync was queued Assert.NotNull(response); Assert.NotNull(response.Status); - Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + Assert.True(response.Status == "queued"); } [GitHubEnterpriseTest] @@ -56,11 +61,13 @@ public class EnterpriseLdapClientTests _github.Enterprise.Ldap.UpdateTeamMapping(_context.TeamId, newLDAPMapping); Assert.NotNull(ldapTeam); + Assert.NotNull(ldapTeam.LdapDn); + Assert.Equal(ldapTeam.LdapDn, _distinguishedNameTeam); // Get Team and check mapping was updated var checkTeam = await _github.Organization.Team.Get(_context.TeamId); Assert.Equal(checkTeam.Name, ldapTeam.Name); - //Assert.Equal(checkTeam.LDAPDN, _fixtureDistinguishedNameTeam); + Assert.Equal(checkTeam.LdapDn, _distinguishedNameTeam); } [GitHubEnterpriseTest] @@ -72,6 +79,11 @@ public class EnterpriseLdapClientTests // Check response message indicates LDAP sync was queued Assert.NotNull(response); Assert.NotNull(response.Status); - Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + Assert.True(response.Status == "queued"); + } + + public void Dispose() + { + _context.Dispose(); } } diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index 99099ac9..1d7c6749 100644 --- a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -8,13 +8,16 @@ using Xunit; namespace Octokit.Tests.Integration { - public class ObservableEnterpriseLdapClientTests + public class ObservableEnterpriseLdapClientTests : IDisposable { readonly IObservableGitHubClient _github; - readonly EnterpriseTeamContext _context; - readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; - readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + readonly string _testUser = "test-user"; + readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; + + readonly EnterpriseTeamContext _context; + readonly string _distinguishedNameTeam = "cn=test-team,ou=groups,dc=company,dc=com"; + public ObservableEnterpriseLdapClientTests() { var gitHub = EnterpriseHelper.GetAuthenticatedClient(); @@ -29,28 +32,30 @@ namespace Octokit.Tests.Integration { var newLDAPMapping = new NewLdapMapping(_distinguishedNameUser); var observable = - _github.Enterprise.Ldap.UpdateUserMapping(EnterpriseHelper.UserName, newLDAPMapping); + _github.Enterprise.Ldap.UpdateUserMapping(_testUser, newLDAPMapping); var ldapUser = await observable; Assert.NotNull(ldapUser); + Assert.NotNull(ldapUser.LdapDn); + Assert.Equal(ldapUser.LdapDn, _distinguishedNameUser); // Get user and check mapping was updated - var checkUser = await _github.User.Get(EnterpriseHelper.UserName); + var checkUser = await _github.User.Get(_testUser); Assert.Equal(checkUser.Login, ldapUser.Login); - //Assert.Equal(checkUser.LdapDN, _distinguishedNameUser); + Assert.Equal(checkUser.LdapDn, _distinguishedNameUser); } [GitHubEnterpriseTest] public async Task CanQueueSyncUserMapping() { var observable = - _github.Enterprise.Ldap.QueueSyncUserMapping(EnterpriseHelper.UserName); + _github.Enterprise.Ldap.QueueSyncUserMapping(_testUser); var response = await observable; // Check response message indicates LDAP sync was queued Assert.NotNull(response); Assert.NotNull(response.Status); - Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + Assert.True(response.Status == "queued"); } [GitHubEnterpriseTest] @@ -62,11 +67,13 @@ namespace Octokit.Tests.Integration var ldapTeam = await observable; Assert.NotNull(ldapTeam); + Assert.NotNull(ldapTeam.LdapDn); + Assert.Equal(ldapTeam.LdapDn, _distinguishedNameTeam); // Get Team and check mapping was updated var checkTeam = await _github.Organization.Team.Get(_context.TeamId); Assert.Equal(checkTeam.Name, ldapTeam.Name); - //Assert.Equal(checkTeam.LdapDN, _distinguishedNameTeam); + Assert.Equal(checkTeam.LdapDn, _distinguishedNameTeam); } [GitHubEnterpriseTest] @@ -79,7 +86,12 @@ namespace Octokit.Tests.Integration // Check response message indicates LDAP sync was queued Assert.NotNull(response); Assert.NotNull(response.Status); - Assert.True(response.Status.All(m => m.Contains("was added to the indexing queue"))); + Assert.True(response.Status == "queued"); + } + + public void Dispose() + { + _context.Dispose(); } } } diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs index ee30b8e6..4d0d0e06 100644 --- a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -20,7 +20,7 @@ namespace Octokit.Tests.Clients string expectedUri = "admin/ldap/users/test-user/mapping"; client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedNameUser)); - connection.Received().Patch(Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); + connection.Received().Patch(Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); } [Fact] @@ -31,10 +31,10 @@ namespace Octokit.Tests.Clients client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedNameUser)); - connection.Received().Patch( + connection.Received().Patch( Arg.Any(), Arg.Is(a => - a.LdapDN == _distinguishedNameUser)); + a.LdapDn == _distinguishedNameUser)); } [Fact] @@ -86,7 +86,7 @@ namespace Octokit.Tests.Clients string expectedUri = "admin/ldap/teams/1/mapping"; client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedNameTeam)); - connection.Received().Patch( + connection.Received().Patch( Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); } @@ -99,10 +99,10 @@ namespace Octokit.Tests.Clients client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedNameTeam)); - connection.Received().Patch( + connection.Received().Patch( Arg.Any(), Arg.Is(a => - a.LdapDN == _distinguishedNameTeam)); + a.LdapDn == _distinguishedNameTeam)); } [Fact] diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index c8779945..c23bfbfc 100644 --- a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -21,7 +21,7 @@ namespace Octokit.Tests github.Enterprise.Ldap.Received(1).UpdateUserMapping( Arg.Is(a => a == "test-user"), Arg.Is(a => - a.LdapDN == _distinguishedName)); + a.LdapDn == _distinguishedName)); } } @@ -41,7 +41,7 @@ namespace Octokit.Tests public class TheUpdateTeamMappingMethod { - readonly string _distinguishedName = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + readonly string _distinguishedName = "cn=test-team,ou=groups,dc=company,dc=com"; [Fact] public void CallsIntoClient() @@ -53,7 +53,7 @@ namespace Octokit.Tests github.Enterprise.Ldap.Received(1).UpdateTeamMapping( Arg.Is(a => a == 1), Arg.Is(a => - a.LdapDN == _distinguishedName)); + a.LdapDn == _distinguishedName)); } } From fcd42b1a53b5a9029af460112053d50a7d950e00 Mon Sep 17 00:00:00 2001 From: Mordechai Zuber Date: Tue, 16 Feb 2016 22:34:57 +0200 Subject: [PATCH 10/18] IDEs do not prevent stupid --- Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs b/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs index 2c82e24e..6a47e8bb 100644 --- a/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs +++ b/Octokit.Tests.Integration/Helpers/ReferenceExtensionsTests.cs @@ -24,8 +24,8 @@ namespace Octokit.Tests.Integration.Helpers var allBranchNames = (await client.Repository.GetAllBranches(context.RepositoryOwner, context.RepositoryName)).Select(b => b.Name); - Assert.Contains("patch-1", allBrancheNames); - Assert.Contains("patch-2", allBrancheNames); + Assert.Contains("patch-1", allBranchNames); + Assert.Contains("patch-2", allBranchNames); } } } From 5e16030dd4109e4091fe3458652baa906581505a Mon Sep 17 00:00:00 2001 From: Mordechai Zuber Date: Wed, 17 Feb 2016 06:46:38 +0200 Subject: [PATCH 11/18] Check branch name to be properly formed --- Octokit/Helpers/ReferenceExtensions.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Octokit/Helpers/ReferenceExtensions.cs b/Octokit/Helpers/ReferenceExtensions.cs index c3bcc165..9f0f952a 100644 --- a/Octokit/Helpers/ReferenceExtensions.cs +++ b/Octokit/Helpers/ReferenceExtensions.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Octokit.Helpers { @@ -22,6 +23,11 @@ namespace Octokit.Helpers Ensure.ArgumentNotNullOrEmptyString(branchName, "branchName"); Ensure.ArgumentNotNull(baseReference, "baseReference"); + if (branchName.StartsWith("refs/heads")) + { + throw new ArgumentException("The branchName seems to be misformed. Try using the Create method"); + } + return await referencesClient.Create(owner, name, new NewReference("refs/heads/" + branchName, baseReference.Object.Sha)); } @@ -38,6 +44,11 @@ namespace Octokit.Helpers Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNullOrEmptyString(branchName, "branchName"); + if (branchName.StartsWith("refs/heads")) + { + throw new ArgumentException("The branchName seems to be misformed. Try using the Create method"); + } + var baseBranch = await referencesClient.Get(owner, name, "heads/master"); return await referencesClient.Create(owner, name, new NewReference("refs/heads/" + branchName, baseBranch.Object.Sha)); } From 6a0ef92a84fe4dab1801eda5481dfa0b12f6885b Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Wed, 17 Feb 2016 22:55:11 +1000 Subject: [PATCH 12/18] Rename LdapDn to LdapDistinguishedName and use [Parameter(Key = "ldap_dn")] to set the API field name --- .../Enterprise/EnterpriseLdapClientTests.cs | 12 ++++++------ .../ObservableEnterpriseLdapClientTests.cs | 12 ++++++------ .../Enterprise/EnterpriseLdapClientTests.cs | 4 ++-- .../ObservableEnterpriseLdapClientTests.cs | 4 ++-- .../Request/Enterprise/NewLdapMapping.cs | 18 +++++++++--------- Octokit/Models/Response/Team.cs | 12 +++++------- Octokit/Models/Response/User.cs | 12 +++++------- 7 files changed, 35 insertions(+), 39 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs index da837b17..edbca496 100644 --- a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -32,13 +32,13 @@ public class EnterpriseLdapClientTests : IDisposable _github.Enterprise.Ldap.UpdateUserMapping(_testUser, newLDAPMapping); Assert.NotNull(ldapUser); - Assert.NotNull(ldapUser.LdapDn); - Assert.Equal(ldapUser.LdapDn, _distinguishedNameUser); + Assert.NotNull(ldapUser.LdapDistinguishedName); + Assert.Equal(ldapUser.LdapDistinguishedName, _distinguishedNameUser); // Get user and check mapping was updated var checkUser = await _github.User.Get(_testUser); Assert.Equal(checkUser.Login, ldapUser.Login); - Assert.Equal(checkUser.LdapDn, _distinguishedNameUser); + Assert.Equal(checkUser.LdapDistinguishedName, _distinguishedNameUser); } [GitHubEnterpriseTest] @@ -61,13 +61,13 @@ public class EnterpriseLdapClientTests : IDisposable _github.Enterprise.Ldap.UpdateTeamMapping(_context.TeamId, newLDAPMapping); Assert.NotNull(ldapTeam); - Assert.NotNull(ldapTeam.LdapDn); - Assert.Equal(ldapTeam.LdapDn, _distinguishedNameTeam); + Assert.NotNull(ldapTeam.LdapDistinguishedName); + Assert.Equal(ldapTeam.LdapDistinguishedName, _distinguishedNameTeam); // Get Team and check mapping was updated var checkTeam = await _github.Organization.Team.Get(_context.TeamId); Assert.Equal(checkTeam.Name, ldapTeam.Name); - Assert.Equal(checkTeam.LdapDn, _distinguishedNameTeam); + Assert.Equal(checkTeam.LdapDistinguishedName, _distinguishedNameTeam); } [GitHubEnterpriseTest] diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index 1d7c6749..08806162 100644 --- a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -36,13 +36,13 @@ namespace Octokit.Tests.Integration var ldapUser = await observable; Assert.NotNull(ldapUser); - Assert.NotNull(ldapUser.LdapDn); - Assert.Equal(ldapUser.LdapDn, _distinguishedNameUser); + Assert.NotNull(ldapUser.LdapDistinguishedName); + Assert.Equal(ldapUser.LdapDistinguishedName, _distinguishedNameUser); // Get user and check mapping was updated var checkUser = await _github.User.Get(_testUser); Assert.Equal(checkUser.Login, ldapUser.Login); - Assert.Equal(checkUser.LdapDn, _distinguishedNameUser); + Assert.Equal(checkUser.LdapDistinguishedName, _distinguishedNameUser); } [GitHubEnterpriseTest] @@ -67,13 +67,13 @@ namespace Octokit.Tests.Integration var ldapTeam = await observable; Assert.NotNull(ldapTeam); - Assert.NotNull(ldapTeam.LdapDn); - Assert.Equal(ldapTeam.LdapDn, _distinguishedNameTeam); + Assert.NotNull(ldapTeam.LdapDistinguishedName); + Assert.Equal(ldapTeam.LdapDistinguishedName, _distinguishedNameTeam); // Get Team and check mapping was updated var checkTeam = await _github.Organization.Team.Get(_context.TeamId); Assert.Equal(checkTeam.Name, ldapTeam.Name); - Assert.Equal(checkTeam.LdapDn, _distinguishedNameTeam); + Assert.Equal(checkTeam.LdapDistinguishedName, _distinguishedNameTeam); } [GitHubEnterpriseTest] diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs index 4d0d0e06..d5ea1c4d 100644 --- a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -34,7 +34,7 @@ namespace Octokit.Tests.Clients connection.Received().Patch( Arg.Any(), Arg.Is(a => - a.LdapDn == _distinguishedNameUser)); + a.LdapDistinguishedName == _distinguishedNameUser)); } [Fact] @@ -102,7 +102,7 @@ namespace Octokit.Tests.Clients connection.Received().Patch( Arg.Any(), Arg.Is(a => - a.LdapDn == _distinguishedNameTeam)); + a.LdapDistinguishedName == _distinguishedNameTeam)); } [Fact] diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index c23bfbfc..a1fc9769 100644 --- a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -21,7 +21,7 @@ namespace Octokit.Tests github.Enterprise.Ldap.Received(1).UpdateUserMapping( Arg.Is(a => a == "test-user"), Arg.Is(a => - a.LdapDn == _distinguishedName)); + a.LdapDistinguishedName == _distinguishedName)); } } @@ -53,7 +53,7 @@ namespace Octokit.Tests github.Enterprise.Ldap.Received(1).UpdateTeamMapping( Arg.Is(a => a == 1), Arg.Is(a => - a.LdapDn == _distinguishedName)); + a.LdapDistinguishedName == _distinguishedName)); } } diff --git a/Octokit/Models/Request/Enterprise/NewLdapMapping.cs b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs index f660a7a1..d5832728 100644 --- a/Octokit/Models/Request/Enterprise/NewLdapMapping.cs +++ b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using Octokit.Internal; namespace Octokit { @@ -15,26 +16,25 @@ namespace Octokit /// /// Initializes a new instance of the class. /// - /// The LDAP Distinguished Name - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] - public NewLdapMapping(string ldapDn) + /// The LDAP Distinguished Name + public NewLdapMapping(string ldapDistinguishedName) { - LdapDn = ldapDn; + Ensure.ArgumentNotNullOrEmptyString(ldapDistinguishedName, "ldapDistinguishedName"); + + LdapDistinguishedName = ldapDistinguishedName; } /// /// The LDAP Distinguished Name (required) /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] - public string LdapDn { get; private set; } + [Parameter(Key = "ldap_dn")] + public string LdapDistinguishedName { get; private set; } internal string DebuggerDisplay { get { - return string.Format(CultureInfo.InvariantCulture, "LdapDn: {0}", LdapDn); + return string.Format(CultureInfo.InvariantCulture, "LdapDistinguishedName: {0}", LdapDistinguishedName); } } } diff --git a/Octokit/Models/Response/Team.cs b/Octokit/Models/Response/Team.cs index 60f54946..0cb7d7df 100644 --- a/Octokit/Models/Response/Team.cs +++ b/Octokit/Models/Response/Team.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using Octokit.Internal; namespace Octokit { @@ -13,9 +14,7 @@ namespace Octokit { public Team() { } - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Dn")] - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] - public Team(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization, string ldapDn) + public Team(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization, string ldapDistinguishedName) { Url = url; Id = id; @@ -24,7 +23,7 @@ namespace Octokit MembersCount = membersCount; ReposCount = reposCount; Organization = organization; - LdapDn = ldapDn; + LdapDistinguishedName = ldapDistinguishedName; } /// @@ -65,9 +64,8 @@ namespace Octokit /// /// LDAP Binding (GitHub Enterprise only) /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] - public string LdapDn { get; protected set; } + [Parameter(Key = "ldap_dn")] + public string LdapDistinguishedName { get; protected set; } internal string DebuggerDisplay { diff --git a/Octokit/Models/Response/User.cs b/Octokit/Models/Response/User.cs index 4eb745ae..14bc3e96 100644 --- a/Octokit/Models/Response/User.cs +++ b/Octokit/Models/Response/User.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using Octokit.Internal; namespace Octokit { @@ -13,13 +14,11 @@ namespace Octokit { public User() { } - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] - public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDn) + public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDistinguishedName) : base(avatarUrl, bio, blog, collaborators, company, createdAt, diskUsage, email, followers, following, hireable, htmlUrl, totalPrivateRepos, id, location, login, name, ownedPrivateRepos, plan, privateGists, publicGists, publicRepos, AccountType.User, url) { SiteAdmin = siteAdmin; - LdapDn = ldapDn; + LdapDistinguishedName = ldapDistinguishedName; } /// @@ -30,9 +29,8 @@ namespace Octokit /// /// LDAP Binding (GitHub Enterprise only) /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dn")] - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Dn")] - public string LdapDn { get; protected set; } + [Parameter(Key = "ldap_dn")] + public string LdapDistinguishedName { get; protected set; } internal string DebuggerDisplay { From 8c120466dd56b9ee892868be325edaae33a200d0 Mon Sep 17 00:00:00 2001 From: Mordechai Zuber Date: Thu, 18 Feb 2016 09:07:00 +0200 Subject: [PATCH 13/18] Argue with man who plays :soccer:, do not --- Octokit/Helpers/ReferenceExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Octokit/Helpers/ReferenceExtensions.cs b/Octokit/Helpers/ReferenceExtensions.cs index 9f0f952a..93bd4f70 100644 --- a/Octokit/Helpers/ReferenceExtensions.cs +++ b/Octokit/Helpers/ReferenceExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Threading.Tasks; namespace Octokit.Helpers @@ -25,7 +26,7 @@ namespace Octokit.Helpers if (branchName.StartsWith("refs/heads")) { - throw new ArgumentException("The branchName seems to be misformed. Try using the Create method"); + throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The specified branch name '{0}' appears to be a ref name and not a branch name because it starts with the string 'refs/heads'. Either specify just the branch name or use the Create method if you need to specify the full ref name", branchName), "branchName"); } return await referencesClient.Create(owner, name, new NewReference("refs/heads/" + branchName, baseReference.Object.Sha)); @@ -46,7 +47,7 @@ namespace Octokit.Helpers if (branchName.StartsWith("refs/heads")) { - throw new ArgumentException("The branchName seems to be misformed. Try using the Create method"); + throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The specified branch name '{0}' appears to be a ref name and not a branch name because it starts with the string 'refs/heads'. Either specify just the branch name or use the Create method if you need to specify the full ref name", branchName), "branchName"); } var baseBranch = await referencesClient.Get(owner, name, "heads/master"); From 9c6c7847b9aec7ea0b80f97dd81db50b9b8cbb8a Mon Sep 17 00:00:00 2001 From: Michael Kokorin Date: Fri, 19 Feb 2016 12:46:42 +0600 Subject: [PATCH 14/18] Fix passing wrong reference in GetArchive method of RepositoryContentsClient --- .../Clients/RepositoryContentsClientTests.cs | 17 +++++++++++++++++ Octokit/Clients/RepositoryContentsClient.cs | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Octokit.Tests/Clients/RepositoryContentsClientTests.cs b/Octokit.Tests/Clients/RepositoryContentsClientTests.cs index 85707ee9..0e59b533 100644 --- a/Octokit.Tests/Clients/RepositoryContentsClientTests.cs +++ b/Octokit.Tests/Clients/RepositoryContentsClientTests.cs @@ -219,5 +219,22 @@ namespace Octokit.Tests.Clients await Assert.ThrowsAsync(() => client.UpdateFile("org", "repo", "path/to/file", null)); } } + + public class TheGetArchiveMethod + { + [Fact] + public void EnsurePassingCorrectParameters() + { + var connection = Substitute.For(); + var client = new RepositoryContentsClient(connection); + + client.GetArchive("org", "repo", ArchiveFormat.Tarball, "dev"); + + const string expectedUri = "repos/org/repo/tarball/dev"; + var expectedTimeSpan = TimeSpan.FromMinutes(60); + + connection.Connection.Received().Get(Arg.Is(uri => uri.ToString() == expectedUri), Arg.Is(span => span == expectedTimeSpan)); + } + } } } \ No newline at end of file diff --git a/Octokit/Clients/RepositoryContentsClient.cs b/Octokit/Clients/RepositoryContentsClient.cs index d7770db4..7f3081c2 100644 --- a/Octokit/Clients/RepositoryContentsClient.cs +++ b/Octokit/Clients/RepositoryContentsClient.cs @@ -238,7 +238,7 @@ namespace Octokit /// The binary contents of the archive public Task GetArchive(string owner, string name, ArchiveFormat archiveFormat, string reference) { - return GetArchive(owner, name, archiveFormat, string.Empty, TimeSpan.FromMinutes(60)); + return GetArchive(owner, name, archiveFormat, reference, TimeSpan.FromMinutes(60)); } /// From 6a61ec7ab1b4e67e47ad8d82580656ea736a332f Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Mon, 22 Feb 2016 18:37:19 +1000 Subject: [PATCH 15/18] Remove unnecessary async/await --- Octokit/Clients/Enterprise/EnterpriseLdapClient.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs index 0073384e..6357da50 100644 --- a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs @@ -24,14 +24,14 @@ namespace Octokit /// The username to update LDAP mapping /// The /// The object. - public async Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) + public Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) { Ensure.ArgumentNotNull(userName, "userName"); Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); var endpoint = ApiUrls.EnterpriseLdapUserMapping(userName); - return await ApiConnection.Patch(endpoint, newLdapMapping); + return ApiConnection.Patch(endpoint, newLdapMapping); } /// @@ -66,14 +66,14 @@ namespace Octokit /// The teamId to update LDAP mapping /// The /// The object. - public async Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) + public Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) { Ensure.ArgumentNotNull(teamId, "teamId"); Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); var endpoint = ApiUrls.EnterpriseLdapTeamMapping(teamId); - return await ApiConnection.Patch(endpoint, newLdapMapping); + return ApiConnection.Patch(endpoint, newLdapMapping); } /// From 1b3fef81f26b9f4972f82c359d63438c4baf54bc Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sun, 14 Feb 2016 00:59:30 +1000 Subject: [PATCH 16/18] Initial implementation of missing UserOrganization client methods --- .../Clients/UserAdministrationClientTests.cs | 249 +++++++++++++++++- Octokit/Clients/IUserAdministrationClient.cs | 117 ++++++-- Octokit/Clients/UserAdministrationClient.cs | 179 +++++++++++-- Octokit/Helpers/ApiUrls.cs | 33 ++- .../Models/Request/NewImpersonationToken.cs | 38 +++ Octokit/Models/Request/NewUser.cs | 48 ++++ Octokit/Models/Request/UserRename.cs | 38 +++ Octokit/Models/Response/User.cs | 13 +- Octokit/Models/Response/UserRenameResponse.cs | 38 +++ Octokit/Octokit-Mono.csproj | 4 + Octokit/Octokit-MonoAndroid.csproj | 4 + Octokit/Octokit-Monotouch.csproj | 4 + Octokit/Octokit-Portable.csproj | 4 + Octokit/Octokit-netcore45.csproj | 4 + Octokit/Octokit.csproj | 6 +- 15 files changed, 734 insertions(+), 45 deletions(-) create mode 100644 Octokit/Models/Request/NewImpersonationToken.cs create mode 100644 Octokit/Models/Request/NewUser.cs create mode 100644 Octokit/Models/Request/UserRename.cs create mode 100644 Octokit/Models/Response/UserRenameResponse.cs diff --git a/Octokit.Tests/Clients/UserAdministrationClientTests.cs b/Octokit.Tests/Clients/UserAdministrationClientTests.cs index 573fe107..4174b06b 100644 --- a/Octokit.Tests/Clients/UserAdministrationClientTests.cs +++ b/Octokit.Tests/Clients/UserAdministrationClientTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using NSubstitute; using Octokit.Tests.Helpers; @@ -9,6 +10,174 @@ namespace Octokit.Tests.Clients { public class UserAdministrationClientTests { + public class TheCreateMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new UserAdministrationClient(Substitute.For()); + await Assert.ThrowsAsync(() => client.Create(null)); + } + + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + var expectedUri = "admin/users"; + client.Create(new NewUser("name", "email@company.com")); + + connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri), + Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + client.Create(new NewUser("name", "email@company.com")); + + connection.Received().Post( + Arg.Any(), + Arg.Is(a => + a.Login == "name" && + a.Email == "email@company.com")); + } + } + + public class TheRenameMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new UserAdministrationClient(Substitute.For()); + await Assert.ThrowsAsync(() => client.Rename(null, new UserRename("newlogin"))); + await Assert.ThrowsAsync(() => client.Rename("login", null)); + } + + [Fact] + public async Task EnsuresNonEmptyString() + { + var client = new UserAdministrationClient(Substitute.For()); + var exception = await Assert.ThrowsAsync(() => client.Rename("", new UserRename())); + Assert.Equal("login", exception.ParamName); + } + + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + var expectedUri = "admin/users/auser"; + client.Rename("auser", new UserRename()); + + connection.Received().Patch( + Arg.Is(u => u.ToString() == expectedUri), + Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + client.Rename("auser", new UserRename("newlogin")); + + connection.Received().Patch( + Arg.Any(), + Arg.Is(a => + a.Login == "newlogin")); + } + } + + public class TheCreateImpersonationTokenMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new UserAdministrationClient(Substitute.For()); + await Assert.ThrowsAsync(() => client.CreateImpersonationToken(null, new NewImpersonationToken())); + await Assert.ThrowsAsync(() => client.CreateImpersonationToken("login", null)); + } + + [Fact] + public async Task EnsuresNonEmptyString() + { + var client = new UserAdministrationClient(Substitute.For()); + var exception = await Assert.ThrowsAsync(() => client.CreateImpersonationToken("", new NewImpersonationToken())); + Assert.Equal("login", exception.ParamName); + } + + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + var expectedUri = "admin/users/auser/authorizations"; + + client.CreateImpersonationToken("auser", new NewImpersonationToken()); + + connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri), + Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + string[] scopes = new string[] { "public-repo" }; + client.CreateImpersonationToken("auser", new NewImpersonationToken(scopes)); + + connection.Received().Post( + Arg.Any(), + Arg.Is(a => + a.Scopes.Count() == scopes.Count() && + a.Scopes.ToList().All(s => scopes.Contains(s)) && + scopes.ToList().All(s => a.Scopes.Contains(s)))); + } + } + + public class TheDeleteImpersonationTokenMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new UserAdministrationClient(Substitute.For()); + await Assert.ThrowsAsync(() => client.DeleteImpersonationToken(null)); + } + + [Fact] + public async Task EnsuresNonEmptyString() + { + var client = new UserAdministrationClient(Substitute.For()); + var exception = await Assert.ThrowsAsync(() => client.DeleteImpersonationToken("")); + Assert.Equal("login", exception.ParamName); + } + + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + var expectedUri = "admin/users/auser/authorizations"; + client.DeleteImpersonationToken("auser"); + + connection.Connection.Received().Delete( + Arg.Is(u => u.ToString() == expectedUri)); + } + } + public class ThePromoteMethod { [Fact] @@ -32,11 +201,14 @@ namespace Octokit.Tests.Clients var connection = Substitute.For(); var client = new UserAdministrationClient(connection); + var expectedUri = "users/auser/site_admin"; client.Promote("auser"); - connection.Received().Put(Arg.Is(u => u.ToString() == "/users/auser/site_admin")); + connection.Received().Put( + Arg.Is(u => u.ToString() == expectedUri)); } } + public class TheDemoteMethod { [Fact] @@ -60,9 +232,11 @@ namespace Octokit.Tests.Clients var connection = Substitute.For(); var client = new UserAdministrationClient(connection); + var expectedUri = "users/auser/site_admin"; client.Demote("auser"); - connection.Received().Delete(Arg.Is(u => u.ToString() == "/users/auser/site_admin")); + connection.Received().Delete( + Arg.Is(u => u.ToString() == expectedUri)); } } @@ -89,9 +263,11 @@ namespace Octokit.Tests.Clients var connection = Substitute.For(); var client = new UserAdministrationClient(connection); + var expectedUri = "users/auser/suspended"; client.Suspend("auser"); - connection.Received().Put(Arg.Is(u => u.ToString() == "/users/auser/suspended")); + connection.Received().Put( + Arg.Is(u => u.ToString() == expectedUri)); } } @@ -118,9 +294,74 @@ namespace Octokit.Tests.Clients var connection = Substitute.For(); var client = new UserAdministrationClient(connection); + var expectedUri = "users/auser/suspended"; client.Unsuspend("auser"); - connection.Received().Delete(Arg.Is(u => u.ToString() == "/users/auser/suspended")); + connection.Received().Delete( + Arg.Is(u => u.ToString() == expectedUri)); + } + } + + public class TheListAllPublicKeysMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + var expectedUri = "admin/keys"; + client.ListAllPublicKeys(); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == expectedUri)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new UserAdministrationClient(Substitute.For()); + await Assert.ThrowsAsync(() => client.Delete(null)); + } + + [Fact] + public async Task EnsuresNonEmptyString() + { + var client = new UserAdministrationClient(Substitute.For()); + var exception = await Assert.ThrowsAsync(() => client.Delete("")); + Assert.Equal("login", exception.ParamName); + } + + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + var expectedUri = "admin/users/auser"; + client.Delete("auser"); + + connection.Connection.Received().Delete( + Arg.Is(u => u.ToString() == expectedUri)); + } + } + + public class TheDeletePublicKeyMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new UserAdministrationClient(connection); + + var expectedUri = "admin/keys/1"; + client.DeletePublicKey(1); + + connection.Connection.Received().Delete( + Arg.Is(u => u.ToString() == expectedUri)); } } } diff --git a/Octokit/Clients/IUserAdministrationClient.cs b/Octokit/Clients/IUserAdministrationClient.cs index cdececd9..8bd2af8e 100644 --- a/Octokit/Clients/IUserAdministrationClient.cs +++ b/Octokit/Clients/IUserAdministrationClient.cs @@ -1,56 +1,137 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; using System.Threading.Tasks; namespace Octokit -{ /// - /// A client for GitHub's User Administration API. - /// - /// - /// See the Administration API documentation for more details. - /// +{ + /// + /// A client for GitHub's User Administration API (GitHub Enterprise) + /// + /// + /// See the Administration API documentation for more details. + /// public interface IUserAdministrationClient { /// - /// 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 + Task 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 + Task 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 + Task 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 + /// + Task 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. /// Task 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. /// Task 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. /// Task 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. /// Task Unsuspend(string login); + + /// + /// List all public keys (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// + Task> ListAllPublicKeys(); + + /// + /// Delete a user (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to delete + /// + Task Delete(string login); + + /// + /// Delete a public key (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The key to delete + /// + Task DeletePublicKey(int keyId); } } diff --git a/Octokit/Clients/UserAdministrationClient.cs b/Octokit/Clients/UserAdministrationClient.cs index 4b8ff4c8..a39fcb8d 100644 --- a/Octokit/Clients/UserAdministrationClient.cs +++ b/Octokit/Clients/UserAdministrationClient.cs @@ -1,13 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; namespace Octokit { /// - /// 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. @@ -24,63 +22,210 @@ namespace Octokit } /// - /// 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 Task Create(NewUser newUser) + { + Ensure.ArgumentNotNull(newUser, "newUser"); + + var endpoint = ApiUrls.UserAdministration(); + + return ApiConnection.Post(endpoint, 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 + public Task Rename(string login, UserRename userRename) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + Ensure.ArgumentNotNull(userRename, "userRename"); + + var endpoint = ApiUrls.UserAdministration(login); + + return ApiConnection.Patch(endpoint, 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 + public Task CreateImpersonationToken(string login, NewImpersonationToken newImpersonationToken) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + Ensure.ArgumentNotNull(newImpersonationToken, "newImpersonationToken"); + + var endpoint = ApiUrls.UserAdministrationAuthorization(login); + + return ApiConnection.Post(endpoint, 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 + /// + public async Task DeleteImpersonationToken(string login) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + + var endpoint = ApiUrls.UserAdministrationAuthorization(login); + + var response = ((HttpStatusCode)await Connection.Delete(endpoint)); + if (response != HttpStatusCode.NoContent) + { + throw new ApiException("Invalid Status Code returned. Expected a 204", response); + } + + return; + } + + /// + /// 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. /// public Task Promote(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - var endpoint = ApiUrls.UserAdministration(login); + var endpoint = ApiUrls.UserAdministrationSiteAdmin(login); return ApiConnection.Put(endpoint); } /// - /// 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. /// public Task Demote(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - var endpoint = ApiUrls.UserAdministration(login); + var endpoint = ApiUrls.UserAdministrationSiteAdmin(login); return ApiConnection.Delete(endpoint); } /// - /// 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. /// public Task Suspend(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - var endpoint = ApiUrls.UserSuspension(login); + var endpoint = ApiUrls.UserAdministrationSuspension(login); return ApiConnection.Put(endpoint); } /// - /// 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. /// public Task Unsuspend(string login) { Ensure.ArgumentNotNullOrEmptyString(login, "login"); - var endpoint = ApiUrls.UserSuspension(login); + var endpoint = ApiUrls.UserAdministrationSuspension(login); return ApiConnection.Delete(endpoint); } + + /// + /// List all public keys (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// + public Task> ListAllPublicKeys() + { + var endpoint = ApiUrls.UserAdministrationPublicKeys(); + return ApiConnection.GetAll(endpoint); + } + + /// + /// Delete a user (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The user to delete + /// + public async Task Delete(string login) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + var endpoint = ApiUrls.UserAdministration(login); + + var response = ((HttpStatusCode)await Connection.Delete(endpoint)); + if (response != HttpStatusCode.NoContent) + { + throw new ApiException("Invalid Status Code returned. Expected a 204", response); + } + + return; + } + + /// + /// Delete a public key (must be Site Admin user). + /// + /// + /// See the API documentation + /// for more information. + /// + /// The key to delete + /// + public async Task DeletePublicKey(int keyId) + { + Ensure.ArgumentNotNull(keyId, "keyId"); + var endpoint = ApiUrls.UserAdministrationPublicKeys(keyId); + + var response = ((HttpStatusCode)await Connection.Delete(endpoint)); + if (response != HttpStatusCode.NoContent) + { + throw new ApiException("Invalid Status Code returned. Expected a 204", response); + } + + return; + } } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 2c7e131c..1f067602 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -1699,14 +1699,39 @@ namespace Octokit return "staff/indexing_jobs".FormatUri(); } + public static Uri UserAdministration() + { + return "admin/users".FormatUri(); + } + + public static Uri UserAdministration(string login) + { + return "admin/users/{0}".FormatUri(login); + } + + public static Uri UserAdministrationAuthorization(string login) + { + return "admin/users/{0}/authorizations".FormatUri(login); + } + + public static Uri UserAdministrationPublicKeys() + { + return "admin/keys".FormatUri(); + } + + public static Uri UserAdministrationPublicKeys(int keyId) + { + return "admin/keys/{0}".FormatUri(keyId); + } + /// /// Creates the relative for altering administration status of a user. /// /// The login for the intended user. /// - public static Uri UserAdministration(string login) + public static Uri UserAdministrationSiteAdmin(string login) { - return "/users/{0}/site_admin".FormatUri(login); + return "users/{0}/site_admin".FormatUri(login); } /// @@ -1714,9 +1739,9 @@ namespace Octokit /// /// The login for the intended user. /// - public static Uri UserSuspension(string login) + public static Uri UserAdministrationSuspension(string login) { - return "/users/{0}/suspended".FormatUri(login); + return "users/{0}/suspended".FormatUri(login); } } } diff --git a/Octokit/Models/Request/NewImpersonationToken.cs b/Octokit/Models/Request/NewImpersonationToken.cs new file mode 100644 index 00000000..3e55126d --- /dev/null +++ b/Octokit/Models/Request/NewImpersonationToken.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Describes a new Impersonation Token to create via the method. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewImpersonationToken + { + public NewImpersonationToken() { } + + /// + /// Initializes a new instance of the class. + /// + /// The scopes for the token. + public NewImpersonationToken(IEnumerable scopes) + { + Scopes = scopes; + } + + /// + /// The scopes for the token + /// + public IEnumerable Scopes { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Scopes: {0}", string.Join("\r\n", Scopes)); + } + } + } +} diff --git a/Octokit/Models/Request/NewUser.cs b/Octokit/Models/Request/NewUser.cs new file mode 100644 index 00000000..600d339d --- /dev/null +++ b/Octokit/Models/Request/NewUser.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace Octokit +{ + /// + /// Describes a new user to create via the method. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewUser + { + public NewUser() { } + + /// + /// Initializes a new instance of the class. + /// + /// The login for the user. + /// The email address of the user + public NewUser(string login, string email) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + Ensure.ArgumentNotNullOrEmptyString(email, "email"); + + Login = login; + Email = email; + } + + /// + /// Login of the user + /// + public string Login { get; protected set; } + + /// + /// Email address of the user + /// + public string Email { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Login: {0} Email: {1}", Login, Email); + } + } + } +} diff --git a/Octokit/Models/Request/UserRename.cs b/Octokit/Models/Request/UserRename.cs new file mode 100644 index 00000000..6a7bde76 --- /dev/null +++ b/Octokit/Models/Request/UserRename.cs @@ -0,0 +1,38 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Describes the new login when renaming a user via the method. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class UserRename + { + public UserRename() { } + + /// + /// Initializes a new instance of the class. + /// + /// The new login for the user. + public UserRename(string login) + { + Ensure.ArgumentNotNullOrEmptyString(login, "login"); + + Login = login; + } + + /// + /// The new username for the user + /// + public string Login { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Login: {0}", Login); + } + } + } +} diff --git a/Octokit/Models/Response/User.cs b/Octokit/Models/Response/User.cs index 14bc3e96..59bb4e36 100644 --- a/Octokit/Models/Response/User.cs +++ b/Octokit/Models/Response/User.cs @@ -14,11 +14,12 @@ namespace Octokit { public User() { } - public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDistinguishedName) + public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDistinguishedName, DateTimeOffset? suspendedAt) : base(avatarUrl, bio, blog, collaborators, company, createdAt, diskUsage, email, followers, following, hireable, htmlUrl, totalPrivateRepos, id, location, login, name, ownedPrivateRepos, plan, privateGists, publicGists, publicRepos, AccountType.User, url) { SiteAdmin = siteAdmin; LdapDistinguishedName = ldapDistinguishedName; + SuspendedAt = suspendedAt; } /// @@ -26,6 +27,16 @@ namespace Octokit /// public bool SiteAdmin { get; protected set; } + /// + /// When the user was suspended, if at all (GitHub Enterprise) + /// + public DateTimeOffset? SuspendedAt { get; protected set; } + + /// + /// Whether or not the user is currently suspended + /// + public bool Suspended { get { return SuspendedAt.HasValue; } } + /// /// LDAP Binding (GitHub Enterprise only) /// diff --git a/Octokit/Models/Response/UserRenameResponse.cs b/Octokit/Models/Response/UserRenameResponse.cs new file mode 100644 index 00000000..9fe0c8a6 --- /dev/null +++ b/Octokit/Models/Response/UserRenameResponse.cs @@ -0,0 +1,38 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents the response information from a operation + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class UserRenameResponse + { + public UserRenameResponse() { } + + public UserRenameResponse(string message, string url) + { + Message = message; + Url = url; + } + + /// + /// Message indiating if the Rename request was queued + /// + public string Message { get; protected set; } + + /// + /// Url to the user that will be renamed + /// + public string Url { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Message: {0}", Message); + } + } + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 674e22ac..1adf178b 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -452,6 +452,10 @@ + + + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 15d146fc..96b6ba41 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -460,6 +460,10 @@ + + + + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 98e4beee..491e5cbb 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -456,6 +456,10 @@ + + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index eedca311..f378486f 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -449,6 +449,10 @@ + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 8478f6f5..67015b13 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -456,6 +456,10 @@ + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 45a35727..0109e67a 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -113,12 +113,14 @@ + + @@ -132,6 +134,7 @@ + @@ -171,6 +174,7 @@ + @@ -488,4 +492,4 @@ --> - + \ No newline at end of file From c56588d394860a3578c3f225dda714f3c4bda06a Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sun, 14 Feb 2016 00:49:37 +1000 Subject: [PATCH 17/18] Add integration tests and context helper for creating/destroying users on GitHub Enterprise during integration tests --- .../Clients/UserAdministrationClientTests.cs | 196 ++++++++++++++++++ Octokit.Tests.Integration/EnterpriseHelper.cs | 16 ++ .../Helpers/EnterpriseUserContext.cs | 30 +++ .../Helpers/GithubClientExtensions.cs | 7 + .../Octokit.Tests.Integration.csproj | 2 + 5 files changed, 251 insertions(+) create mode 100644 Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs create mode 100644 Octokit.Tests.Integration/Helpers/EnterpriseUserContext.cs diff --git a/Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs b/Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs new file mode 100644 index 00000000..321017ac --- /dev/null +++ b/Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs @@ -0,0 +1,196 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +namespace Octokit.Tests.Integration.Clients +{ + public class UserAdministrationClientTests + { + readonly IGitHubClient _github; + + public UserAdministrationClientTests() + { + _github = 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 user = await + _github.User.Administration.Create(newUser); + + // 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 response = await _github.User.Administration.Rename( + context.UserLogin, + new UserRename(renamedUsername)); + + 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 token = await _github.User.Administration.CreateImpersonationToken( + context.UserLogin, + new NewImpersonationToken(new string[] { "public_repo" })); + + 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 keys = await _github.User.Administration.ListAllPublicKeys(); + + 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.Integration/EnterpriseHelper.cs b/Octokit.Tests.Integration/EnterpriseHelper.cs index 63323012..7e03a604 100644 --- a/Octokit.Tests.Integration/EnterpriseHelper.cs +++ b/Octokit.Tests.Integration/EnterpriseHelper.cs @@ -140,6 +140,22 @@ namespace Octokit.Tests.Integration catch { } } + public static void DeleteUser(User user) + { + if (user != null) + DeleteUser(user.Login); + } + + public static void DeleteUser(string username) + { + var api = GetAuthenticatedClient(); + try + { + api.User.Administration.Delete(username).Wait(TimeSpan.FromSeconds(15)); + } + catch { } + } + public static IGitHubClient GetAuthenticatedClient() { return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl) diff --git a/Octokit.Tests.Integration/Helpers/EnterpriseUserContext.cs b/Octokit.Tests.Integration/Helpers/EnterpriseUserContext.cs new file mode 100644 index 00000000..496fb52d --- /dev/null +++ b/Octokit.Tests.Integration/Helpers/EnterpriseUserContext.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit.Tests.Integration.Helpers +{ + internal sealed class EnterpriseUserContext : IDisposable + { + internal EnterpriseUserContext(User user) + { + User = user; + UserId = user.Id; + UserLogin = user.Login; + UserEmail = user.Email; + } + + internal int UserId { get; private set; } + internal string UserLogin { get; private set; } + internal string UserEmail { get; private set; } + + internal User User { get; private set; } + + public void Dispose() + { + EnterpriseHelper.DeleteUser(User); + } + } +} diff --git a/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs b/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs index badd2b56..e262aba0 100644 --- a/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs +++ b/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs @@ -36,5 +36,12 @@ namespace Octokit.Tests.Integration.Helpers return new EnterpriseTeamContext(team); } + + internal async static Task CreateEnterpriseUserContext(this IGitHubClient 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 d12eff12..9e3f07e7 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -105,6 +105,7 @@ + @@ -112,6 +113,7 @@ + From bd10379846ace99266788cd6c7c71a7630864fed Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sun, 14 Feb 2016 23:17:06 +1000 Subject: [PATCH 18/18] 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 d893f91d..844a0e15 100644 --- a/Octokit.Reactive/Clients/IObservableUserAdministrationClient.cs +++ b/Octokit.Reactive/Clients/IObservableUserAdministrationClient.cs @@ -5,7 +5,7 @@ using System.Reactive; 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 d76d7e9d..bc0bf672 100644 --- a/Octokit.Reactive/Clients/ObservableUserAdministrationClient.cs +++ b/Octokit.Reactive/Clients/ObservableUserAdministrationClient.cs @@ -7,9 +7,16 @@ using System.Reactive.Linq; 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 @@ namespace Octokit.Reactive 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 @@ namespace Octokit.Reactive } /// - /// 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 @@ namespace Octokit.Reactive } /// - /// 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 @@ namespace Octokit.Reactive } /// - /// 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 @@ namespace Octokit.Reactive { 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 8a5919ad..030795b0 100644 --- a/Octokit.Tests.Integration/Helpers/ObservableGithubClientExtensions.cs +++ b/Octokit.Tests.Integration/Helpers/ObservableGithubClientExtensions.cs @@ -27,5 +27,19 @@ namespace Octokit.Tests.Integration.Helpers 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 9e3f07e7..3900738a 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 08806162..dde932d8 100644 --- a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -20,11 +20,10 @@ namespace Octokit.Tests.Integration 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 00000000..efaea9a0 --- /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 d605e718..d247ddb3 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 @@ namespace Octokit.Tests.Reactive public class TheDemoteMethod { [Fact] - public void GetsFromClientDemoteDemote() + public void CallsIntoClient() { var gitHubClient = Substitute.For(); var client = new ObservableUserAdministrationClient(gitHubClient); @@ -38,7 +104,7 @@ namespace Octokit.Tests.Reactive public class TheSuspendMethod { [Fact] - public void GetsFromClientSuspendSuspend() + public void CallsIntoClient() { var gitHubClient = Substitute.For(); var client = new ObservableUserAdministrationClient(gitHubClient); @@ -52,7 +118,7 @@ namespace Octokit.Tests.Reactive public class TheUnsuspendMethod { [Fact] - public void GetsFromClientUnsuspendUnsuspend() + public void CallsIntoClient() { var gitHubClient = Substitute.For(); var client = new ObservableUserAdministrationClient(gitHubClient); @@ -63,6 +129,54 @@ namespace Octokit.Tests.Reactive } } + 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]