1
0
mirror of https://github.com/bitwarden/server synced 2026-01-03 09:03:44 +00:00

[PM-28260] Optimize bulk reinvite endpoint (#6670)

* Implement optimized bulk invite resend command

- Added IBulkResendOrganizationInvitesCommand interface to define the bulk resend operation.
- Created BulkResendOrganizationInvitesCommand class to handle the logic for resending invites to multiple organization users.
- Integrated logging and validation to ensure only valid users receive invites.
- Included error handling for non-existent organizations and invalid user statuses.

* Add unit tests for BulkResendOrganizationInvitesCommand

- Implemented comprehensive test cases for the BulkResendOrganizationInvitesCommand class.
- Validated user statuses and ensured correct handling of valid and invalid users during bulk invite resends.
- Included tests for scenarios such as organization not found and empty user lists.
- Utilized Xunit and NSubstitute for effective testing and mocking of dependencies.

* Add IBulkResendOrganizationInvitesCommand to service collection

- Registered IBulkResendOrganizationInvitesCommand in the service collection for dependency injection.

* Update OrganizationUsersController to utilize IBulkResendOrganizationInvitesCommand

- Added IBulkResendOrganizationInvitesCommand to the OrganizationUsersController for handling bulk invite resends based on feature flag.
- Updated BulkReinvite method to conditionally use the new command or the legacy service based on the feature flag status.
- Enhanced unit tests to verify correct command usage depending on feature flag state, ensuring robust testing for both scenarios.
This commit is contained in:
Rui Tomé
2025-12-05 16:28:04 +00:00
committed by GitHub
parent 18a8829476
commit 5469d8be0e
6 changed files with 282 additions and 1 deletions

View File

@@ -0,0 +1,69 @@
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
using Bit.Core.AdminConsole.Utilities.DebuggingInstruments;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Microsoft.Extensions.Logging;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
public class BulkResendOrganizationInvitesCommand : IBulkResendOrganizationInvitesCommand
{
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly ISendOrganizationInvitesCommand _sendOrganizationInvitesCommand;
private readonly ILogger<BulkResendOrganizationInvitesCommand> _logger;
public BulkResendOrganizationInvitesCommand(
IOrganizationUserRepository organizationUserRepository,
IOrganizationRepository organizationRepository,
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand,
ILogger<BulkResendOrganizationInvitesCommand> logger)
{
_organizationUserRepository = organizationUserRepository;
_organizationRepository = organizationRepository;
_sendOrganizationInvitesCommand = sendOrganizationInvitesCommand;
_logger = logger;
}
public async Task<IEnumerable<Tuple<OrganizationUser, string>>> BulkResendInvitesAsync(
Guid organizationId,
Guid? invitingUserId,
IEnumerable<Guid> organizationUsersId)
{
var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUsersId);
_logger.LogUserInviteStateDiagnostics(orgUsers);
var org = await _organizationRepository.GetByIdAsync(organizationId);
if (org == null)
{
throw new NotFoundException();
}
var validUsers = new List<OrganizationUser>();
var result = new List<Tuple<OrganizationUser, string>>();
foreach (var orgUser in orgUsers)
{
if (orgUser.Status != OrganizationUserStatusType.Invited || orgUser.OrganizationId != organizationId)
{
result.Add(Tuple.Create(orgUser, "User invalid."));
}
else
{
validUsers.Add(orgUser);
}
}
if (validUsers.Any())
{
await _sendOrganizationInvitesCommand.SendInvitesAsync(
new SendInvitesRequest(validUsers, org));
result.AddRange(validUsers.Select(u => Tuple.Create(u, "")));
}
return result;
}
}

View File

@@ -0,0 +1,20 @@
using Bit.Core.Entities;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
public interface IBulkResendOrganizationInvitesCommand
{
/// <summary>
/// Resend invites to multiple organization users in bulk.
/// </summary>
/// <param name="organizationId">The ID of the organization.</param>
/// <param name="invitingUserId">The ID of the user who is resending the invites.</param>
/// <param name="organizationUsersId">The IDs of the organization users to resend invites to.</param>
/// <returns>A tuple containing the OrganizationUser and an error message (empty string if successful)</returns>
Task<IEnumerable<Tuple<OrganizationUser, string>>> BulkResendInvitesAsync(
Guid organizationId,
Guid? invitingUserId,
IEnumerable<Guid> organizationUsersId);
}