Skip to content

Commit

Permalink
feat: Add create organization invitation via email (#2895)
Browse files Browse the repository at this point in the history
* Add API request to create org invitation

* Add tests for create org invitation

* Add IObservable API request to create org invitation

* Add tests for IObservable create org invitation
  • Loading branch information
skyemcleman committed Mar 11, 2024
1 parent d27d1f0 commit 41d5d24
Show file tree
Hide file tree
Showing 13 changed files with 464 additions and 3 deletions.
15 changes: 15 additions & 0 deletions Octokit.Reactive/Clients/IObservableOrganizationMembersClient.cs
Expand Up @@ -304,6 +304,21 @@ public interface IObservableOrganizationMembersClient
/// <returns></returns>
IObservable<OrganizationMembership> AddOrUpdateOrganizationMembership(string org, string user, OrganizationMembershipUpdate addOrUpdateRequest);

/// <summary>
/// Create an organization invitation for a user
/// </summary>
/// <remarks>
/// This method requires authentication.
/// The authenticated user must be an organization owner.
/// See the <a href="https://developer.github.com/v3/orgs/members/#create-an-organization-invitation">API documentation</a>
/// for more information.
/// </remarks>
/// <param name="org">The login for the organization</param>
/// <param name="invitationRequest">An <see cref="OrganizationInvitationRequest"/> instance containing the
/// details of the organization invitation</param>
/// <returns></returns>
IObservable<OrganizationMembershipInvitation> CreateOrganizationInvitation(string org, OrganizationInvitationRequest invitationRequest);

/// <summary>
/// Remove a user's membership with an organization.
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions Octokit.Reactive/Clients/ObservableOrganizationMembersClient.cs
Expand Up @@ -418,6 +418,27 @@ public IObservable<OrganizationMembership> AddOrUpdateOrganizationMembership(str

return _client.AddOrUpdateOrganizationMembership(org, user, addOrUpdateRequest).ToObservable();
}

/// <summary>
/// Create an organization invitation for a user
/// </summary>
/// <remarks>
/// This method requires authentication.
/// The authenticated user must be an organization owner.
/// See the <a href="https://developer.github.com/v3/orgs/members/#create-an-organization-invitation">API documentation</a>
/// for more information.
/// </remarks>
/// <param name="org">The login for the organization</param>
/// <param name="invitationRequest">An <see cref="OrganizationInvitationRequest"/> instance containing the
/// details of the organization invitation</param>
/// <returns></returns>
public IObservable<OrganizationMembershipInvitation> CreateOrganizationInvitation(string org, OrganizationInvitationRequest invitationRequest)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(invitationRequest, nameof(invitationRequest));

return _client.CreateOrganizationInvitation(org, invitationRequest).ToObservable();
}

/// <summary>
/// Remove a user's membership with an organization.
Expand Down
@@ -1,5 +1,7 @@
using System.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
using Octokit.Tests.Helpers;
using Octokit.Tests.Integration.Helpers;
using Xunit;

Expand Down Expand Up @@ -172,6 +174,95 @@ public async Task ReturnsUsersPendingAdminOrganizationMembership()
}
}

public class TheCreateOrganizationInvitationMethod
{
readonly IGitHubClient _gitHub;

public TheCreateOrganizationInvitationMethod()
{
_gitHub = Helper.GetAuthenticatedClient();
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationViaUserId()
{
var user = await _gitHub.User.Get("alfhenrik-test-2");

var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id);
var organizationMembershipInvitation = await _gitHub.Organization.Member.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal("alfhenrik-test-2", organizationMembershipInvitation.Login);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);

await _gitHub.Organization.Member.RemoveOrganizationMembership(Helper.Organization, "alfhenrik-test-2");
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationViaUserEmail()
{
var email = RandomEmailGenerator.GenerateRandomEmail();

var organizationInvitationRequest = new OrganizationInvitationRequest(email);
var organizationMembershipInvitation = await _gitHub.Organization.Member.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal(email, organizationMembershipInvitation.Email);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);

await _gitHub.Organization.Member.CancelOrganizationInvitation(Helper.Organization, organizationMembershipInvitation.Id);
}

[OrganizationTest]
public async Task ThrowsApiValidationExceptionForCurrentOrganizationMembers()
{
var user = await _gitHub.User.Get(Helper.UserName);
var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id);

await Assert.ThrowsAsync<ApiValidationException>(() => _gitHub.Organization.Member.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest));
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationSingleTeam()
{
var user = await _gitHub.User.Get("alfhenrik-test-2");

var team1 = await _gitHub.Organization.Team.Create(Helper.Organization, new NewTeam("TestTeam1"));

var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id, new int[] {team1.Id});
var organizationMembershipInvitation = await _gitHub.Organization.Member.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal("alfhenrik-test-2", organizationMembershipInvitation.Login);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);
Assert.Equal(1, organizationMembershipInvitation.TeamCount);

await _gitHub.Organization.Team.Delete(Helper.Organization, team1.Slug);
await _gitHub.Organization.Member.RemoveOrganizationMembership(Helper.Organization, "alfhenrik-test-2");
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationMultipleTeams()
{
var user = await _gitHub.User.Get("alfhenrik-test-2");

var team1 = await _gitHub.Organization.Team.Create(Helper.Organization, new NewTeam("TestTeam1"));
var team2 = await _gitHub.Organization.Team.Create(Helper.Organization, new NewTeam("TestTeam2"));

var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id, new int[] {team1.Id, team2.Id});
var organizationMembershipInvitation = await _gitHub.Organization.Member.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal("alfhenrik-test-2", organizationMembershipInvitation.Login);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);
Assert.Equal(2, organizationMembershipInvitation.TeamCount);

await _gitHub.Organization.Team.Delete(Helper.Organization, team1.Slug);
await _gitHub.Organization.Team.Delete(Helper.Organization, team2.Slug);
await _gitHub.Organization.Member.RemoveOrganizationMembership(Helper.Organization, "alfhenrik-test-2");
}
}

public class TheRemoveOrganizationMembershipMethod
{
readonly IGitHubClient _gitHub;
Expand Down
@@ -1,7 +1,9 @@
using System.Reactive.Linq;
using System;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Threading.Tasks;
using Octokit.Reactive;
using Octokit.Tests.Helpers;
using Octokit.Tests.Integration.Helpers;
using Xunit;

Expand Down Expand Up @@ -62,6 +64,97 @@ public async Task ReturnsUsersPendingAdminOrganizationMembership()
await _client.RemoveOrganizationMembership(Helper.Organization, "alfhenrik-test-2");
}
}

public class TheCreateOrganizationInvitationMethod
{
readonly IGitHubClient _gitHub;
readonly ObservableOrganizationMembersClient _client;

public TheCreateOrganizationInvitationMethod()
{
_gitHub = Helper.GetAuthenticatedClient();
_client = new ObservableOrganizationMembersClient(_gitHub);
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationViaUserId()
{
var user = await _gitHub.User.Get("alfhenrik-test-2");

var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id);
var organizationMembershipInvitation = await _client.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal("alfhenrik-test-2", organizationMembershipInvitation.Login);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);

await _client.RemoveOrganizationMembership(Helper.Organization, "alfhenrik-test-2");
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationViaUserEmail()
{
var email = RandomEmailGenerator.GenerateRandomEmail();

var organizationInvitationRequest = new OrganizationInvitationRequest(email);
var organizationMembershipInvitation = await _client.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal(email, organizationMembershipInvitation.Email);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);

await _client.CancelOrganizationInvitation(Helper.Organization, organizationMembershipInvitation.Id);
}

[OrganizationTest]
public async Task ThrowsApiValidationExceptionForCurrentOrganizationMembers()
{
var user = await _gitHub.User.Get(Helper.UserName);
var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id);

await Assert.ThrowsAsync<ApiValidationException>(() => _client.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest).ToTask());
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationSingleTeam()
{
var user = await _gitHub.User.Get("alfhenrik-test-2");

var team1 = await _gitHub.Organization.Team.Create(Helper.Organization, new NewTeam("TestTeam1"));

var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id, new int[] {team1.Id});
var organizationMembershipInvitation = await _client.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal("alfhenrik-test-2", organizationMembershipInvitation.Login);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);
Assert.Equal(1, organizationMembershipInvitation.TeamCount);

await _gitHub.Organization.Team.Delete(Helper.Organization, team1.Slug);
await _client.RemoveOrganizationMembership(Helper.Organization, "alfhenrik-test-2");
}

[OrganizationTest]
public async Task ReturnsOrganizationMembershipInvitationMultipleTeams()
{
var user = await _gitHub.User.Get("alfhenrik-test-2");

var team1 = await _gitHub.Organization.Team.Create(Helper.Organization, new NewTeam("TestTeam1"));
var team2 = await _gitHub.Organization.Team.Create(Helper.Organization, new NewTeam("TestTeam2"));

var organizationInvitationRequest = new OrganizationInvitationRequest(user.Id, new int[] {team1.Id, team2.Id});
var organizationMembershipInvitation = await _client.CreateOrganizationInvitation(Helper.Organization, organizationInvitationRequest);

Assert.Equal("alfhenrik-test-2", organizationMembershipInvitation.Login);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationMembershipInvitation.Role.Value);
Assert.Equal(Helper.UserName, organizationMembershipInvitation.Inviter.Login);
Assert.Equal(2, organizationMembershipInvitation.TeamCount);

await _gitHub.Organization.Team.Delete(Helper.Organization, team1.Slug);
await _gitHub.Organization.Team.Delete(Helper.Organization, team2.Slug);
await _client.RemoveOrganizationMembership(Helper.Organization, "alfhenrik-test-2");
}
}

public class TheRemoveOrganizationMembershipMethod
{
Expand Down
28 changes: 28 additions & 0 deletions Octokit.Tests/Clients/OrganizationMembersClientTests.cs
Expand Up @@ -2,6 +2,7 @@
using System.Net;
using System.Threading.Tasks;
using NSubstitute;
using NSubstitute.Core.DependencyInjection;
using Octokit.Internal;
using Xunit;

Expand Down Expand Up @@ -562,6 +563,33 @@ public async Task EnsureNonNullArguments()
}
}

public class TheCreateOrganizationInvitationMethod
{
[Fact]
public void PostsToTheCorrectUrl()
{
var organizationInvitationRequest = new OrganizationInvitationRequest("email");

var connection = Substitute.For<IApiConnection>();
var client = new OrganizationMembersClient(connection);

client.CreateOrganizationInvitation("org", organizationInvitationRequest);

connection.Received().Post<OrganizationMembershipInvitation>(Arg.Is<Uri>(u => u.ToString() == "orgs/org/invitations"), Arg.Any<object>());
}

[Fact]
public async Task EnsureNonNullArguments()
{
var organizationInvitationRequest = new OrganizationInvitationRequest("email");
var client = new OrganizationMembersClient(Substitute.For<IApiConnection>());

await Assert.ThrowsAsync<ArgumentNullException>(() => client.CreateOrganizationInvitation(null, organizationInvitationRequest));
await Assert.ThrowsAsync<ArgumentException>(() => client.CreateOrganizationInvitation("", organizationInvitationRequest));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.CreateOrganizationInvitation("org", null));
}
}

public class TheDeleteOrganizationMembershipMethod
{
[Fact]
Expand Down
15 changes: 15 additions & 0 deletions Octokit.Tests/Helpers/RandomEmailGenerator.cs
@@ -0,0 +1,15 @@
using System;

namespace Octokit.Tests.Helpers
{
public static class RandomEmailGenerator
{
public static string GenerateRandomEmail()
{
var randomUsername = Guid.NewGuid().ToString();
var randomDomain = Guid.NewGuid().ToString();

return $"{randomUsername}@{randomDomain}.com";
}
}
}
35 changes: 35 additions & 0 deletions Octokit.Tests/Models/OrganizationInvitationRequestTests.cs
@@ -0,0 +1,35 @@
using System.Threading.Tasks;
using Xunit;

namespace Octokit.Tests.Models
{
public class OrganizationInvitationRequestTests
{
public class TheConstructor
{
[Fact]
public void CreatesOrganizationInvitationRequestByUserId()
{
const int userId = 1;
var organizationInvitationRequest = new OrganizationInvitationRequest(userId);

Assert.Equal(userId, organizationInvitationRequest.InviteeId);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationInvitationRequest.Role);
Assert.Null(organizationInvitationRequest.Email);
Assert.Null(organizationInvitationRequest.TeamIds);
}

[Fact]
public async Task CreatesOrganizationInvitationRequestByUserEmail()
{
const string email = "testemail";
var organizationInvitationRequest = new OrganizationInvitationRequest(email);

Assert.Equal(email, organizationInvitationRequest.Email);
Assert.Equal(OrganizationMembershipRole.DirectMember, organizationInvitationRequest.Role);
Assert.Null(organizationInvitationRequest.InviteeId);
Assert.Null(organizationInvitationRequest.TeamIds);
}
}
}
}
27 changes: 27 additions & 0 deletions Octokit.Tests/Reactive/ObservableOrganizationMembersClientTests.cs
Expand Up @@ -361,6 +361,33 @@ public async Task EnsuresNonNullArguments()
await Assert.ThrowsAsync<ArgumentNullException>(() => client.AddOrUpdateOrganizationMembership("org", "username", null).ToTask());
}
}

public class TheCreateOrganizationInvitationMethod
{
[Fact]
public void CreateOrganizationInvitationFromClientOrganizationMember()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationMembersClient(gitHubClient);

var organizationInvitationRequest = new OrganizationInvitationRequest(1);
client.CreateOrganizationInvitation("org", organizationInvitationRequest);

gitHubClient.Organization.Member.Received().CreateOrganizationInvitation("org", organizationInvitationRequest);
}

[Fact]
public async Task EnsureNonNullArguments()
{
var client = new ObservableOrganizationMembersClient(Substitute.For<IGitHubClient>());

var organizationInvitationRequest = new OrganizationInvitationRequest(1);

await Assert.ThrowsAsync<ArgumentNullException>(() => client.CreateOrganizationInvitation(null, organizationInvitationRequest).ToTask());
await Assert.ThrowsAsync<ArgumentException>(() => client.CreateOrganizationInvitation("", organizationInvitationRequest).ToTask());
await Assert.ThrowsAsync<ArgumentNullException>(() => client.CreateOrganizationInvitation("org", null).ToTask());
}
}

public class TheDeleteOrganizationMembershipMethod
{
Expand Down

0 comments on commit 41d5d24

Please sign in to comment.