diff --git a/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs b/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs index 8d77cad6..924e7674 100644 --- a/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs +++ b/Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs @@ -273,5 +273,28 @@ namespace Octokit.Reactive /// The login for the user /// IObservable Conceal(string org, string user); + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// + IObservable GetAllPendingInvitations(string org); + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// Options to change API behaviour + /// + IObservable GetAllPendingInvitations(string org, ApiOptions options); } } diff --git a/Octokit.Reactive/Clients/IObservableTeamsClient.cs b/Octokit.Reactive/Clients/IObservableTeamsClient.cs index 908960b9..a5555c60 100644 --- a/Octokit.Reactive/Clients/IObservableTeamsClient.cs +++ b/Octokit.Reactive/Clients/IObservableTeamsClient.cs @@ -196,5 +196,28 @@ namespace Octokit.Reactive /// /// if the repository is managed by the given team; otherwise. IObservable IsRepositoryManagedByTeam(int id, string owner, string repo); + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// + IObservable GetAllPendingInvitations(int id); + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// Options to change API behaviour. + /// + IObservable GetAllPendingInvitations(int id, ApiOptions options); } } diff --git a/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs index 56bde28b..6107f94a 100644 --- a/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs +++ b/Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs @@ -375,5 +375,39 @@ namespace Octokit.Reactive return _client.Conceal(org, user).ToObservable(); } + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// + public IObservable GetAllPendingInvitations(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + + return GetAllPendingInvitations(org, ApiOptions.None); + } + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// Options to change API behaviour + /// + public IObservable GetAllPendingInvitations(string org, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _connection.GetAndFlattenAllPages(ApiUrls.OrganizationPendingInvititations(org), null, AcceptHeaders.OrganizationMembershipPreview, options); + } } } diff --git a/Octokit.Reactive/Clients/ObservableTeamsClient.cs b/Octokit.Reactive/Clients/ObservableTeamsClient.cs index 0cb63575..e6297e28 100644 --- a/Octokit.Reactive/Clients/ObservableTeamsClient.cs +++ b/Octokit.Reactive/Clients/ObservableTeamsClient.cs @@ -295,5 +295,39 @@ namespace Octokit.Reactive Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); return _client.IsRepositoryManagedByTeam(id, owner, repo).ToObservable(); } + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// + public IObservable GetAllPendingInvitations(int id) + { + Ensure.ArgumentNotNull(id, nameof(id)); + + return GetAllPendingInvitations(id, ApiOptions.None); + } + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// Options to change API behaviour + /// + public IObservable GetAllPendingInvitations(int id, ApiOptions options) + { + Ensure.ArgumentNotNull(id, nameof(id)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _connection.GetAndFlattenAllPages(ApiUrls.TeamPendingInvitations(id), null, AcceptHeaders.OrganizationMembershipPreview, options); + } } } diff --git a/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs b/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs index 63ea01b0..bde9817a 100644 --- a/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs +++ b/Octokit.Tests.Integration/Clients/OrganizationMembersClientTests.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using Octokit.Tests.Integration.Helpers; using Xunit; namespace Octokit.Tests.Integration.Clients @@ -17,7 +18,7 @@ namespace Octokit.Tests.Integration.Clients _organizationFixture = "octokit"; } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsMembers() { var members = await @@ -25,7 +26,7 @@ namespace Octokit.Tests.Integration.Clients Assert.NotEmpty(members); } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfMembersWithoutStart() { var options = new ApiOptions @@ -39,7 +40,7 @@ namespace Octokit.Tests.Integration.Clients Assert.Equal(1, members.Count); } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfMembersWithStart() { var options = new ApiOptions @@ -54,7 +55,7 @@ namespace Octokit.Tests.Integration.Clients Assert.Equal(1, members.Count); } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsDistinctMembersBasedOnStartPage() { var startOptions = new ApiOptions @@ -119,5 +120,91 @@ namespace Octokit.Tests.Integration.Clients Assert.True(membersWithNo2FA.Count <= memberCount); } } + + public class TheGetAllPendingInvitationsMethod + { + readonly IGitHubClient _gitHub; + + public TheGetAllPendingInvitationsMethod() + { + _gitHub = Helper.GetAuthenticatedClient(); + } + + [OrganizationTest] + public async Task ReturnsNoPendingInvitations() + { + var pendingInvitations = await _gitHub.Organization.Member.GetAllPendingInvitations(Helper.Organization); + Assert.NotNull(pendingInvitations); + Assert.Empty(pendingInvitations); + } + + [OrganizationTest] + public async Task ReturnsPendingInvitations() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var pendingInvitations = await _gitHub.Organization.Member.GetAllPendingInvitations(Helper.Organization); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(2, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithoutStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + + var pendingInvitations = await _gitHub.Organization.Member.GetAllPendingInvitations(Helper.Organization, options); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(1, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var firstPageOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var firstPagePendingInvitations = await _gitHub.Organization.Member.GetAllPendingInvitations(Helper.Organization, firstPageOptions); + Assert.NotEmpty(firstPagePendingInvitations); + Assert.Equal(1, firstPagePendingInvitations.Count); + + var secondPageOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + + var secondPagePendingInvitations = await _gitHub.Organization.Member.GetAllPendingInvitations(Helper.Organization, secondPageOptions); + Assert.NotEmpty(secondPagePendingInvitations); + Assert.Equal(1, secondPagePendingInvitations.Count); + + Assert.NotEqual(firstPagePendingInvitations[0].Login, secondPagePendingInvitations[0].Login); + } + } + } } } diff --git a/Octokit.Tests.Integration/Clients/OrganizationOutsideCollaboratorsClientTests.cs b/Octokit.Tests.Integration/Clients/OrganizationOutsideCollaboratorsClientTests.cs index d6e4b4bf..55b86ca0 100644 --- a/Octokit.Tests.Integration/Clients/OrganizationOutsideCollaboratorsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/OrganizationOutsideCollaboratorsClientTests.cs @@ -10,13 +10,13 @@ namespace Octokit.Tests.Integration.Clients public class TheGetAllMethod { readonly IGitHubClient _gitHub; - readonly string _fixtureCollaborator = "alfhenrik-test-2"; + readonly string _fixtureCollaborator = "octokitnet-test1"; public TheGetAllMethod() { _gitHub = Helper.GetAuthenticatedClient(); } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsNoOutsideCollaborators() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); @@ -31,7 +31,7 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsOutsideCollaborators() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); @@ -48,7 +48,7 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithoutStart() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); @@ -71,14 +71,14 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithStart() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var options = new ApiOptions { @@ -96,14 +96,14 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithAllFilter() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var outsideCollaborators = await _gitHub.Organization .OutsideCollaborator @@ -114,14 +114,14 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithAllFilterAndApiOptions() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var options = new ApiOptions { @@ -138,14 +138,14 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithAllFilterWithStart() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var firstPageOptions = new ApiOptions { @@ -175,14 +175,14 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithTwoFactorFilter() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var outsideCollaborators = await _gitHub.Organization .OutsideCollaborator @@ -194,14 +194,14 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithTwoFactorFilterAndApiOptions() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var options = new ApiOptions { @@ -223,14 +223,14 @@ namespace Octokit.Tests.Integration.Clients public class TheDeleteMethod { readonly IGitHubClient _gitHub; - readonly string _fixtureCollaborator = "alfhenrik-test-2"; + readonly string _fixtureCollaborator = "octokitnet-test1"; public TheDeleteMethod() { _gitHub = Helper.GetAuthenticatedClient(); } - [IntegrationTest] + [OrganizationTest] public async Task CanRemoveOutsideCollaborator() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); @@ -255,7 +255,7 @@ namespace Octokit.Tests.Integration.Clients } } - [IntegrationTest] + [OrganizationTest] public async Task CannotRemoveMemberOfOrganizationAsOutsideCollaborator() { var ex = await Assert.ThrowsAsync(() @@ -271,14 +271,14 @@ namespace Octokit.Tests.Integration.Clients public class TheConvertFromMemberMethod { readonly IGitHubClient _gitHub; - readonly string _fixtureCollaborator = "alfhenrik-test-2"; + readonly string _fixtureCollaborator = "octokitnet-test1"; public TheConvertFromMemberMethod() { _gitHub = Helper.GetAuthenticatedClient(); } - [IntegrationTest(Skip = "This test relies on https://github.com/octokit/octokit.net/issues/1533 being implemented before being re-enabled as there's currently no way to invite a member to an org")] + [OrganizationTest(Skip = "This test relies on https://github.com/octokit/octokit.net/issues/1533 being implemented before being re-enabled as there's currently no way to invite a member to an org")] public async Task CanConvertOrgMemberToOutsideCollaborator() { var result = await _gitHub.Organization.OutsideCollaborator.ConvertFromMember(Helper.Organization, _fixtureCollaborator); @@ -290,7 +290,7 @@ namespace Octokit.Tests.Integration.Clients Assert.Equal(_fixtureCollaborator, outsideCollaborators[0].Login); } - [IntegrationTest] + [OrganizationTest] public async Task CannotConvertNonOrgMemberToOutsideCollaborator() { var ex = await Assert.ThrowsAsync(() @@ -302,7 +302,7 @@ namespace Octokit.Tests.Integration.Clients StringComparison.OrdinalIgnoreCase)); } - [IntegrationTest] + [OrganizationTest] public async Task CannotConvertLastOrgOwnerToOutsideCollaborator() { var ex = await Assert.ThrowsAsync(() diff --git a/Octokit.Tests.Integration/Clients/RepositoryCollaboratorClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoryCollaboratorClientTests.cs index efb26521..9bd37a9b 100644 --- a/Octokit.Tests.Integration/Clients/RepositoryCollaboratorClientTests.cs +++ b/Octokit.Tests.Integration/Clients/RepositoryCollaboratorClientTests.cs @@ -273,7 +273,7 @@ public class RepositoryCollaboratorClientTests { var fixture = github.Repository.Collaborator; - var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); Assert.Equal(PermissionLevel.Read, permission.Permission); } @@ -289,7 +289,7 @@ public class RepositoryCollaboratorClientTests { var fixture = github.Repository.Collaborator; - var permission = await fixture.ReviewPermission(context.RepositoryId, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryId, "octokitnet-test1"); Assert.Equal(PermissionLevel.Read, permission.Permission); } @@ -306,9 +306,9 @@ public class RepositoryCollaboratorClientTests var fixture = github.Repository.Collaborator; // add a collaborator - await fixture.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + await fixture.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); - var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); Assert.Equal(PermissionLevel.Write, permission.Permission); } @@ -325,9 +325,9 @@ public class RepositoryCollaboratorClientTests var fixture = github.Repository.Collaborator; // add a collaborator - await fixture.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + await fixture.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); - var permission = await fixture.ReviewPermission(context.RepositoryId, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryId, "octokitnet-test1"); Assert.Equal(PermissionLevel.Write, permission.Permission); } @@ -380,7 +380,7 @@ public class RepositoryCollaboratorClientTests { var fixture = github.Repository.Collaborator; - var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); Assert.Equal(PermissionLevel.None, permission.Permission); } @@ -401,7 +401,7 @@ public class RepositoryCollaboratorClientTests { var fixture = github.Repository.Collaborator; - var permission = await fixture.ReviewPermission(context.RepositoryId, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryId, "octokitnet-test1"); Assert.Equal(PermissionLevel.None, permission.Permission); } diff --git a/Octokit.Tests.Integration/Clients/TeamsClientTests.cs b/Octokit.Tests.Integration/Clients/TeamsClientTests.cs index fecb5c1d..d68a68af 100644 --- a/Octokit.Tests.Integration/Clients/TeamsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/TeamsClientTests.cs @@ -178,4 +178,95 @@ public class TeamsClientTests } } } + + public class TheGetAllPendingInvitationsMethod + { + private readonly IGitHubClient _gitHub; + + public TheGetAllPendingInvitationsMethod() + { + _gitHub = Helper.GetAuthenticatedClient(); + } + + [OrganizationTest] + public async Task ReturnsNoPendingInvitations() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + var team = teamContext.Team; + + var pendingInvitations = await _gitHub.Organization.Team.GetAllPendingInvitations(team.Id); + Assert.NotNull(pendingInvitations); + Assert.Empty(pendingInvitations); + } + } + + [OrganizationTest] + public async Task ReturnsPendingInvitations() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var pendingInvitations = await _gitHub.Organization.Team.GetAllPendingInvitations(teamContext.TeamId); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(2, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithoutStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var pendingInvitations = await _gitHub.Organization.Team.GetAllPendingInvitations(teamContext.TeamId, options); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(1, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var firstPageOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + var firstPagePendingInvitations = await _gitHub.Organization.Team.GetAllPendingInvitations(teamContext.TeamId, firstPageOptions); + Assert.NotEmpty(firstPagePendingInvitations); + Assert.Equal(1, firstPagePendingInvitations.Count); + + var secondPageOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPagePendingInvitations = await _gitHub.Organization.Team.GetAllPendingInvitations(teamContext.TeamId, secondPageOptions); + Assert.NotEmpty(secondPagePendingInvitations); + Assert.Equal(1, secondPagePendingInvitations.Count); + + Assert.NotEqual(firstPagePendingInvitations[0].Login, secondPagePendingInvitations[0].Login); + } + } + } } diff --git a/Octokit.Tests.Integration/Helper.cs b/Octokit.Tests.Integration/Helper.cs index 32df12ef..8682f787 100644 --- a/Octokit.Tests.Integration/Helper.cs +++ b/Octokit.Tests.Integration/Helper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; @@ -228,5 +229,29 @@ namespace Octokit.Tests.Integration Credentials = new Credentials(Guid.NewGuid().ToString(), "bad-password") }; } + + public static void DeleteInvitations(IConnection connection, List invitees, int teamId) + { + try + { + foreach (var invitee in invitees) + { + connection.Delete(new Uri($"orgs/{Organization}/memberships/{invitee}", UriKind.Relative), null, AcceptHeaders.OrganizationMembershipPreview).Wait(TimeSpan.FromSeconds(15)); + } + } + catch { } + } + + public static string InviteMemberToTeam(IConnection connection, int teamId, string login) + { + try + { + var client = new GitHubClient(connection); + client.Organization.Team.AddMembership(teamId, login).Wait(TimeSpan.FromSeconds(15)); + } + catch { } + + return login; + } } } diff --git a/Octokit.Tests.Integration/Helpers/TeamContext.cs b/Octokit.Tests.Integration/Helpers/TeamContext.cs index c1479c4c..c04ceb27 100644 --- a/Octokit.Tests.Integration/Helpers/TeamContext.cs +++ b/Octokit.Tests.Integration/Helpers/TeamContext.cs @@ -14,6 +14,7 @@ namespace Octokit.Tests.Integration.Helpers Team = team; TeamId = team.Id; TeamName = team.Name; + Invitations = new List(); } private IConnection _connection; @@ -21,9 +22,18 @@ namespace Octokit.Tests.Integration.Helpers internal string TeamName { get; private set; } internal Team Team { get; private set; } + internal List Invitations { get; private set; } + + public void InviteMember(string login) + { + Invitations.Add(Helper.InviteMemberToTeam(_connection, TeamId, login)); + } public void Dispose() { + if (Invitations.Any()) + Helper.DeleteInvitations(_connection, Invitations, TeamId); + Helper.DeleteTeam(_connection, Team); } } diff --git a/Octokit.Tests.Integration/Reactive/ObservableOrganizationMembersClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableOrganizationMembersClientTests.cs new file mode 100644 index 00000000..aea37f84 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableOrganizationMembersClientTests.cs @@ -0,0 +1,100 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableOrganizationMembersClientTests + { + public class TheGetAllPendingInvitationsMethod + { + readonly IGitHubClient _gitHub; + readonly ObservableOrganizationMembersClient _client; + + public TheGetAllPendingInvitationsMethod() + { + _gitHub = Helper.GetAuthenticatedClient(); + _client = new ObservableOrganizationMembersClient(_gitHub); + } + + [OrganizationTest] + public async Task ReturnsNoPendingInvitations() + { + var pendingInvitations = await _client.GetAllPendingInvitations(Helper.Organization).ToList(); + + Assert.Empty(pendingInvitations); + } + + [OrganizationTest] + public async Task ReturnsPendingInvitations() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var pendingInvitations = await _client.GetAllPendingInvitations(Helper.Organization).ToList(); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(2, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithoutStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var pendingInvitations = await _client.GetAllPendingInvitations(Helper.Organization, options).ToList(); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(1, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var firstPageOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + var firstPagePendingInvitations = await _client.GetAllPendingInvitations(Helper.Organization, firstPageOptions).ToList(); + Assert.NotEmpty(firstPagePendingInvitations); + Assert.Equal(1, firstPagePendingInvitations.Count); + + + var secondPageOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPagePendingInvitations = await _client.GetAllPendingInvitations(Helper.Organization, secondPageOptions).ToList(); + Assert.NotEmpty(secondPagePendingInvitations); + Assert.Equal(1, secondPagePendingInvitations.Count); + + Assert.NotEqual(firstPagePendingInvitations[0].Login, secondPagePendingInvitations[0].Login); + } + } + } + } +} diff --git a/Octokit.Tests.Integration/Reactive/ObservableOrganizationOutsideCollaboratorsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableOrganizationOutsideCollaboratorsClientTests.cs index cd14bdae..ab8d40ed 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableOrganizationOutsideCollaboratorsClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableOrganizationOutsideCollaboratorsClientTests.cs @@ -12,7 +12,7 @@ namespace Octokit.Tests.Integration.Reactive { readonly IGitHubClient _gitHub; readonly ObservableOrganizationOutsideCollaboratorsClient _client; - readonly string _fixtureCollaborator = "alfhenrik-test-2"; + readonly string _fixtureCollaborator = "octokitnet-test1"; public TheGetAllMethod() { @@ -20,7 +20,7 @@ namespace Octokit.Tests.Integration.Reactive _client = new ObservableOrganizationOutsideCollaboratorsClient(_gitHub); } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsNoOutsideCollaborators() { var outsideCollaborators = await _client @@ -30,7 +30,7 @@ namespace Octokit.Tests.Integration.Reactive Assert.Empty(outsideCollaborators); } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsOutsideCollaborators() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); @@ -46,7 +46,7 @@ namespace Octokit.Tests.Integration.Reactive } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithoutStart() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); @@ -68,14 +68,14 @@ namespace Octokit.Tests.Integration.Reactive } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithStart() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var options = new ApiOptions { @@ -92,14 +92,14 @@ namespace Octokit.Tests.Integration.Reactive } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithAllFilter() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var outsideCollaborators = await _client .GetAll(Helper.Organization, OrganizationMembersFilter.All).ToList(); @@ -109,14 +109,14 @@ namespace Octokit.Tests.Integration.Reactive } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithAllFilterAndApiOptions() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var options = new ApiOptions { @@ -132,14 +132,14 @@ namespace Octokit.Tests.Integration.Reactive } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithAllFilterWithStart() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var firstPageOptions = new ApiOptions { @@ -167,32 +167,32 @@ namespace Octokit.Tests.Integration.Reactive } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithTwoFactorFilter() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var outsideCollaborators = await _client .GetAll(Helper.Organization, OrganizationMembersFilter.TwoFactorAuthenticationDisabled).ToList(); Assert.NotNull(outsideCollaborators); Assert.Equal(1, outsideCollaborators.Count); - Assert.Equal("alfhenrik-test-2", outsideCollaborators[0].Login); + Assert.Equal("octokitnet-test1", outsideCollaborators[0].Login); } } - [IntegrationTest] + [OrganizationTest] public async Task ReturnsCorrectCountOfOutsideCollaboratorsWithTwoFactorFilterAndApiOptions() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); using (var context = await _gitHub.CreateRepositoryContext(Helper.Organization, new NewRepository(repoName))) { await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, _fixtureCollaborator); - await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik"); + await _gitHub.Repository.Collaborator.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test2"); var options = new ApiOptions { @@ -205,7 +205,7 @@ namespace Octokit.Tests.Integration.Reactive Assert.NotNull(outsideCollaborators); Assert.Equal(1, outsideCollaborators.Count); - Assert.Equal("alfhenrik-test-2", outsideCollaborators[0].Login); + Assert.Equal("octokitnet-test1", outsideCollaborators[0].Login); } } } @@ -214,7 +214,7 @@ namespace Octokit.Tests.Integration.Reactive { readonly IGitHubClient _gitHub; readonly ObservableOrganizationOutsideCollaboratorsClient _client; - readonly string _fixtureCollaborator = "alfhenrik-test-2"; + readonly string _fixtureCollaborator = "octokitnet-test1"; public TheDeleteMethod() { @@ -222,7 +222,7 @@ namespace Octokit.Tests.Integration.Reactive _client = new ObservableOrganizationOutsideCollaboratorsClient(_gitHub); } - [IntegrationTest] + [OrganizationTest] public async Task CanRemoveOutsideCollaborator() { var repoName = Helper.MakeNameWithTimestamp("public-repo"); @@ -245,7 +245,7 @@ namespace Octokit.Tests.Integration.Reactive { readonly IGitHubClient _gitHub; readonly ObservableOrganizationOutsideCollaboratorsClient _client; - readonly string _fixtureCollaborator = "alfhenrik-test-2"; + readonly string _fixtureCollaborator = "octokitnet-test1"; public TheConvertFromMemberMethod() { @@ -253,7 +253,7 @@ namespace Octokit.Tests.Integration.Reactive _client = new ObservableOrganizationOutsideCollaboratorsClient(_gitHub); } - [IntegrationTest(Skip = "This test relies on https://github.com/octokit/octokit.net/issues/1533 being implemented before being re-enabled as there's currently no way to invite a member to an org")] + [OrganizationTest(Skip = "This test relies on https://github.com/octokit/octokit.net/issues/1533 being implemented before being re-enabled as there's currently no way to invite a member to an org")] public async Task CanConvertOrgMemberToOutsideCollaborator() { var result = await _client.ConvertFromMember(Helper.Organization, _fixtureCollaborator); diff --git a/Octokit.Tests.Integration/Reactive/ObservableRepositoryCollaboratorClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableRepositoryCollaboratorClientTests.cs index f7e5f6f1..a4ec881f 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableRepositoryCollaboratorClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableRepositoryCollaboratorClientTests.cs @@ -152,7 +152,7 @@ public class ObservableRepositoryCollaboratorClientTests { var fixture = new ObservableRepoCollaboratorsClient(github); - var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); Assert.Equal(PermissionLevel.Read, permission.Permission); } @@ -168,7 +168,7 @@ public class ObservableRepositoryCollaboratorClientTests { var fixture = new ObservableRepoCollaboratorsClient(github); - var permission = await fixture.ReviewPermission(context.RepositoryId, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryId, "octokitnet-test1"); Assert.Equal(PermissionLevel.Read, permission.Permission); } @@ -185,9 +185,9 @@ public class ObservableRepositoryCollaboratorClientTests var fixture = new ObservableRepoCollaboratorsClient(github); // add a collaborator - await fixture.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + await fixture.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); - var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); Assert.Equal(PermissionLevel.Write, permission.Permission); } @@ -204,9 +204,9 @@ public class ObservableRepositoryCollaboratorClientTests var fixture = new ObservableRepoCollaboratorsClient(github); // add a collaborator - await fixture.Add(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + await fixture.Add(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); - var permission = await fixture.ReviewPermission(context.RepositoryId, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryId, "octokitnet-test1"); Assert.Equal(PermissionLevel.Write, permission.Permission); } @@ -259,7 +259,7 @@ public class ObservableRepositoryCollaboratorClientTests { var fixture = new ObservableRepoCollaboratorsClient(github); - var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryOwner, context.RepositoryName, "octokitnet-test1"); Assert.Equal(PermissionLevel.None, permission.Permission); } @@ -280,7 +280,7 @@ public class ObservableRepositoryCollaboratorClientTests { var fixture = new ObservableRepoCollaboratorsClient(github); - var permission = await fixture.ReviewPermission(context.RepositoryId, "alfhenrik-test-2"); + var permission = await fixture.ReviewPermission(context.RepositoryId, "octokitnet-test1"); Assert.Equal(PermissionLevel.None, permission.Permission); } diff --git a/Octokit.Tests.Integration/Reactive/ObservableTeamsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableTeamsClientTests.cs index c6b9969d..6c02f885 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableTeamsClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableTeamsClientTests.cs @@ -63,4 +63,99 @@ public class ObservableTeamsClientTests } } } + + public class TheGetAllPendingInvitationsMethod + { + private readonly IGitHubClient _gitHub; + private readonly IObservableTeamsClient _client; + + public TheGetAllPendingInvitationsMethod() + { + _gitHub = Helper.GetAuthenticatedClient(); + _client = new ObservableTeamsClient(_gitHub); + } + + [OrganizationTest] + public async Task ReturnsNoPendingInvitations() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + var team = teamContext.Team; + + var observable = _client.GetAllPendingInvitations(team.Id); + var pendingInvitations = await observable.ToList(); + Assert.NotNull(pendingInvitations); + Assert.Empty(pendingInvitations); + } + } + + [OrganizationTest] + public async Task ReturnsPendingInvitations() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam(Helper.MakeNameWithTimestamp("team")))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var observable = _client.GetAllPendingInvitations(teamContext.TeamId); + var pendingInvitations = await observable.ToList(); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(2, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithoutStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam("team"))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1 + }; + var observable = _client.GetAllPendingInvitations(teamContext.TeamId, options); + var pendingInvitations = await observable.ToList(); + Assert.NotEmpty(pendingInvitations); + Assert.Equal(1, pendingInvitations.Count); + } + } + + [OrganizationTest] + public async Task ReturnsCorrectCountOfPendingInvitationsWithStart() + { + using (var teamContext = await _gitHub.CreateTeamContext(Helper.Organization, new NewTeam("team"))) + { + teamContext.InviteMember("octokitnet-test1"); + teamContext.InviteMember("octokitnet-test2"); + + var firstPageOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + var firstObservable = _client.GetAllPendingInvitations(teamContext.TeamId, firstPageOptions); + var firstPagePendingInvitations = await firstObservable.ToList(); + Assert.NotEmpty(firstPagePendingInvitations); + Assert.Equal(1, firstPagePendingInvitations.Count); + + var secondPageOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 2 + }; + var secondObservable = _client.GetAllPendingInvitations(teamContext.TeamId, secondPageOptions); + var secondPagePendingInvitations = await secondObservable.ToList(); + Assert.NotEmpty(secondPagePendingInvitations); + Assert.Equal(1, secondPagePendingInvitations.Count); + + Assert.NotEqual(firstPagePendingInvitations[0].Login, secondPagePendingInvitations[0].Login); + } + } + } } diff --git a/Octokit.Tests/Clients/OrganizationMembersClientTests.cs b/Octokit.Tests/Clients/OrganizationMembersClientTests.cs index efe95273..09383ebf 100644 --- a/Octokit.Tests/Clients/OrganizationMembersClientTests.cs +++ b/Octokit.Tests/Clients/OrganizationMembersClientTests.cs @@ -494,5 +494,47 @@ namespace Octokit.Tests.Clients await Assert.ThrowsAsync(() => orgMembers.Conceal("org", "")); } } + + public class TheGetAllPendingInvitationsMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationMembersClient(connection); + + client.GetAllPendingInvitations("org"); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/invitations"), null, "application/vnd.github.korra-preview+json", Args.ApiOptions); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new OrganizationMembersClient(connection); + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + client.GetAllPendingInvitations("org", options); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "orgs/org/invitations"), null, "application/vnd.github.korra-preview+json", options); + } + + [Fact] + public async Task EnsureNonNullArguments() + { + var client = new OrganizationMembersClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations(null)); + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations("")); + + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations(null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations("", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations("org", null)); + } + } } } diff --git a/Octokit.Tests/Clients/TeamsClientTests.cs b/Octokit.Tests/Clients/TeamsClientTests.cs index 4f2212e6..f769987f 100644 --- a/Octokit.Tests/Clients/TeamsClientTests.cs +++ b/Octokit.Tests/Clients/TeamsClientTests.cs @@ -344,5 +344,23 @@ namespace Octokit.Tests.Clients await Assert.ThrowsAsync(() => client.IsRepositoryManagedByTeam(1, "ownerName", "")); } } + + public class TheGetAllPendingInvitationsMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new TeamsClient(connection); + + await client.GetAllPendingInvitations(1); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "teams/1/invitations"), + null, + "application/vnd.github.korra-preview+json", + Args.ApiOptions); + } + } } } diff --git a/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs b/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs index 6f4dc49c..740a7339 100644 --- a/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Reactive.Threading.Tasks; using System.Threading.Tasks; +using Octokit.Reactive.Internal; using Xunit; namespace Octokit.Tests.Reactive @@ -306,5 +307,55 @@ namespace Octokit.Tests.Reactive await Assert.ThrowsAsync(() => client.Conceal("org", "").ToTask()); } } + + public class TheGetAllPendingInvitationsMethod + { + [Fact] + public void RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + + client.GetAllPendingInvitations("org"); + + gitHubClient.Connection.Received().GetAndFlattenAllPages( + Arg.Is(u => u.ToString() == "orgs/org/invitations"), + Args.EmptyDictionary, + "application/vnd.github.korra-preview+json"); + } + + [Fact] + public void RequestsTheCorrectUrlWithStart() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationMembersClient(gitHubClient); + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.GetAllPendingInvitations("org", options); + + gitHubClient.Connection.Received().GetAndFlattenAllPages( + Arg.Is(u => u.ToString() == "orgs/org/invitations"), + Arg.Is>(d => d.Count == 2), + "application/vnd.github.korra-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationMembersClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations(null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations("").ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations(null, ApiOptions.None).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations("", ApiOptions.None).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllPendingInvitations("org", null).ToTask()); + } + } } } diff --git a/Octokit.Tests/Reactive/ObservableTeamsClientTests.cs b/Octokit.Tests/Reactive/ObservableTeamsClientTests.cs index 7d7dcf30..eee914fc 100644 --- a/Octokit.Tests/Reactive/ObservableTeamsClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableTeamsClientTests.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Reactive.Threading.Tasks; using System.Threading.Tasks; using NSubstitute; using Octokit.Reactive; +using Octokit.Reactive.Internal; using Xunit; namespace Octokit.Tests.Reactive @@ -43,5 +45,49 @@ namespace Octokit.Tests.Reactive Assert.Throws(() => new ObservableTeamsClient(null)); } } + + public class TheGetAllPendingInvitationsMethod + { + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableTeamsClient(Substitute.For()); + + Assert.Throws(() => client.GetAllPendingInvitations(1, null)); + } + + [Fact] + public void RequestsTheCorrectUrl() + { + var gitHub = Substitute.For(); + var client = new ObservableTeamsClient(gitHub); + + client.GetAllPendingInvitations(1); + + gitHub.Connection.Received().GetAndFlattenAllPages( + Arg.Is(u => u.ToString() == "teams/1/invitations"), + Args.EmptyDictionary, + "application/vnd.github.korra-preview+json"); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var gitHub = Substitute.For(); + var client = new ObservableTeamsClient(gitHub); + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + client.GetAllPendingInvitations(1, options); + + gitHub.Connection.Received().GetAndFlattenAllPages( + Arg.Is(u => u.ToString() == "teams/1/invitations"), + Arg.Is>(d => d.Count == 2), + "application/vnd.github.korra-preview+json"); + } + } } } \ No newline at end of file diff --git a/Octokit/Clients/IOrganizationMembersClient.cs b/Octokit/Clients/IOrganizationMembersClient.cs index c72f5bba..25822818 100644 --- a/Octokit/Clients/IOrganizationMembersClient.cs +++ b/Octokit/Clients/IOrganizationMembersClient.cs @@ -279,5 +279,28 @@ namespace Octokit /// The login for the user /// Task Conceal(string org, string user); + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// + Task> GetAllPendingInvitations(string org); + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// Options to change API behaviour + /// + Task> GetAllPendingInvitations(string org, ApiOptions options); } } diff --git a/Octokit/Clients/ITeamsClient.cs b/Octokit/Clients/ITeamsClient.cs index 940e4be3..7a42619c 100644 --- a/Octokit/Clients/ITeamsClient.cs +++ b/Octokit/Clients/ITeamsClient.cs @@ -186,5 +186,28 @@ namespace Octokit /// /// if the repository is managed by the given team; otherwise. Task IsRepositoryManagedByTeam(int id, string owner, string repo); + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// + Task> GetAllPendingInvitations(int id); + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// Options to change API behaviour. + /// + Task> GetAllPendingInvitations(int id, ApiOptions options); } } diff --git a/Octokit/Clients/OrganizationMembersClient.cs b/Octokit/Clients/OrganizationMembersClient.cs index 5462c0a9..1a3730bf 100644 --- a/Octokit/Clients/OrganizationMembersClient.cs +++ b/Octokit/Clients/OrganizationMembersClient.cs @@ -442,5 +442,39 @@ namespace Octokit return ApiConnection.Delete(ApiUrls.OrganizationMembership(org, user)); } + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// + public Task> GetAllPendingInvitations(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + + return GetAllPendingInvitations(org, ApiOptions.None); + } + + /// + /// List all pending invitations for the organization. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The login for the organization + /// Options to change API behaviour + /// + public Task> GetAllPendingInvitations(string org, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return ApiConnection.GetAll(ApiUrls.OrganizationPendingInvititations(org), null, AcceptHeaders.OrganizationMembershipPreview, options); + } } } diff --git a/Octokit/Clients/TeamsClient.cs b/Octokit/Clients/TeamsClient.cs index eddb83fe..fd2ac393 100644 --- a/Octokit/Clients/TeamsClient.cs +++ b/Octokit/Clients/TeamsClient.cs @@ -380,5 +380,36 @@ namespace Octokit return false; } } + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// + public Task> GetAllPendingInvitations(int id) + { + Ensure.ArgumentNotNull(id, nameof(id)); + + return GetAllPendingInvitations(id, ApiOptions.None); + } + + /// + /// List all pending invitations for the given team. + /// + /// + /// See the API Documentation + /// for more information. + /// + /// The team identifier + /// Options to change API behaviour + /// + public Task> GetAllPendingInvitations(int id, ApiOptions options) + { + return ApiConnection.GetAll(ApiUrls.TeamPendingInvitations(id), null, AcceptHeaders.OrganizationMembershipPreview, options); + } } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index bf6122eb..b8de4229 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -661,6 +661,16 @@ namespace Octokit return "orgs/{0}/public_members/{1}".FormatUri(org, name); } + /// + /// Returns the for the organizations pending invitations + /// + /// The name of the organization + /// + public static Uri OrganizationPendingInvititations(string org) + { + return "orgs/{0}/invitations".FormatUri(org); + } + /// /// Returns the that returns all of the outside collaborators of the organization /// @@ -1509,6 +1519,16 @@ namespace Octokit return "teams/{0}/repos/{1}/{2}".FormatUri(id, organization, repoName); } + /// + /// returns the for the teams pending invitations + /// + /// The team id + /// + public static Uri TeamPendingInvitations(int id) + { + return "teams/{0}/invitations".FormatUri(id); + } + /// /// returns the for teams /// use for update or deleting a team diff --git a/Octokit/Models/Response/OrganizationMembershipInvitation.cs b/Octokit/Models/Response/OrganizationMembershipInvitation.cs new file mode 100644 index 00000000..7d5db500 --- /dev/null +++ b/Octokit/Models/Response/OrganizationMembershipInvitation.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class OrganizationMembershipInvitation + { + public OrganizationMembershipInvitation() + { + } + + public OrganizationMembershipInvitation(int id, string login, string email, OrganizationMembershipRole role, DateTimeOffset createdAt, User inviter) + { + Id = id; + Login = login; + Email = email; + Role = role; + CreatedAt = createdAt; + Inviter = inviter; + } + + public int Id { get; protected set; } + public string Login { get; protected set; } + public string Email { get; protected set; } + public StringEnum Role { get; protected set; } + public DateTimeOffset CreatedAt { get; protected set; } + public User Inviter { get; protected set; } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", + Justification = "Used by DebuggerDisplayAttribute")] + internal string DebuggerDisplay => $"{nameof(OrganizationMembershipInvitation)}: Invitee: {Login ?? "Non-GitHub member"}; Email: {Email}; Inviter: {Inviter.Login}"; + } +} diff --git a/Octokit/Models/Response/OrganizationMembershipRole.cs b/Octokit/Models/Response/OrganizationMembershipRole.cs new file mode 100644 index 00000000..ee74fcfa --- /dev/null +++ b/Octokit/Models/Response/OrganizationMembershipRole.cs @@ -0,0 +1,18 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum OrganizationMembershipRole + { + [Parameter(Value = "direct_member")] + DirectMember, + [Parameter(Value = "admin")] + Admin, + [Parameter(Value = "billing_manager")] + BillingManager, + [Parameter(Value = "hiring_manager")] + HiringManager, + [Parameter(Value = "reinstate")] + Reinstate + } +} \ No newline at end of file