From 90f2e2baeb003692dff540388eef732938274f7c Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Wed, 31 Dec 2025 13:42:32 +1000 Subject: [PATCH] Rename upsert -> create to reflect behavior --- .../ConfirmOrganizationUserCommand.cs | 2 +- .../Repositories/ICollectionRepository.cs | 10 ++++---- .../Repositories/CollectionRepository.cs | 2 +- .../Repositories/CollectionRepository.cs | 4 ++-- .../ConfirmOrganizationUserCommandTests.cs | 4 ++-- ...ts.cs => CreateDefaultCollectionsTests.cs} | 24 +++++++++---------- .../DefaultCollectionSemaphoreTests.cs | 4 ++-- 7 files changed, 24 insertions(+), 26 deletions(-) rename test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/{CollectionRepositoryUpsertDefaultCollectionsTests.cs => CreateDefaultCollectionsTests.cs} (94%) diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs index b6b49e93e9..ed65655477 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs @@ -347,6 +347,6 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand return; } - await _collectionRepository.UpsertDefaultCollectionsAsync(organizationId, eligibleOrganizationUserIds, defaultUserCollectionName); + await _collectionRepository.CreateDefaultCollectionsAsync(organizationId, eligibleOrganizationUserIds, defaultUserCollectionName); } } diff --git a/src/Core/Repositories/ICollectionRepository.cs b/src/Core/Repositories/ICollectionRepository.cs index d64d4f3632..80a55087ea 100644 --- a/src/Core/Repositories/ICollectionRepository.cs +++ b/src/Core/Repositories/ICollectionRepository.cs @@ -64,23 +64,21 @@ public interface ICollectionRepository : IRepository IEnumerable users, IEnumerable groups); /// - /// Creates default user collections for the specified organization users if they do not already have one. - /// Uses the stored procedure approach with semaphore-based duplicate prevention. + /// Creates default user collections for the specified organization users. + /// Throws an exception if any user already has a default collection for the organization. /// /// The Organization ID. /// The Organization User IDs to create default collections for. /// The encrypted string to use as the default collection name. - /// - Task UpsertDefaultCollectionsAsync(Guid organizationId, IEnumerable organizationUserIds, string defaultCollectionName); + Task CreateDefaultCollectionsAsync(Guid organizationId, IEnumerable organizationUserIds, string defaultCollectionName); /// /// Creates default user collections for the specified organization users using bulk insert operations. - /// Inserts semaphore entries before collections to prevent duplicates. + /// Gracefully skips users who already have a default collection for the organization. /// /// The Organization ID. /// The Organization User IDs to create default collections for. /// The encrypted string to use as the default collection name. - /// Task UpsertDefaultCollectionsBulkAsync(Guid organizationId, IEnumerable organizationUserIds, string defaultCollectionName); /// diff --git a/src/Infrastructure.Dapper/Repositories/CollectionRepository.cs b/src/Infrastructure.Dapper/Repositories/CollectionRepository.cs index 584ace3e73..be7f9deda7 100644 --- a/src/Infrastructure.Dapper/Repositories/CollectionRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/CollectionRepository.cs @@ -360,7 +360,7 @@ public class CollectionRepository : Repository, ICollectionRep } } - public async Task UpsertDefaultCollectionsAsync(Guid organizationId, IEnumerable organizationUserIds, string defaultCollectionName) + public async Task CreateDefaultCollectionsAsync(Guid organizationId, IEnumerable organizationUserIds, string defaultCollectionName) { organizationUserIds = organizationUserIds.ToList(); if (!organizationUserIds.Any()) diff --git a/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs b/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs index dc94619df3..8453b99dd6 100644 --- a/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs @@ -795,7 +795,7 @@ public class CollectionRepository : Repository organizationUserIds, string defaultCollectionName) + public async Task CreateDefaultCollectionsAsync(Guid organizationId, IEnumerable organizationUserIds, string defaultCollectionName) { organizationUserIds = organizationUserIds.ToList(); if (!organizationUserIds.Any()) @@ -836,7 +836,7 @@ public class CollectionRepository : Repository organizationUserIds, string defaultCollectionName) { // EF uses the same bulk copy approach as the main method - await UpsertDefaultCollectionsAsync(organizationId, organizationUserIds, defaultCollectionName); + await CreateDefaultCollectionsAsync(organizationId, organizationUserIds, defaultCollectionName); } public async Task> GetDefaultCollectionSemaphoresAsync(Guid organizationId) diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs index 5528ecb2a2..3d5aafbaf3 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs @@ -525,7 +525,7 @@ public class ConfirmOrganizationUserCommandTests await sutProvider.GetDependency() .DidNotReceive() - .UpsertDefaultCollectionsAsync(Arg.Any(), Arg.Any>(), Arg.Any()); + .CreateDefaultCollectionsAsync(Arg.Any(), Arg.Any>(), Arg.Any()); } [Theory, BitAutoData] @@ -560,7 +560,7 @@ public class ConfirmOrganizationUserCommandTests await sutProvider.GetDependency() .DidNotReceive() - .UpsertDefaultCollectionsAsync(Arg.Any(), Arg.Any>(), Arg.Any()); + .CreateDefaultCollectionsAsync(Arg.Any(), Arg.Any>(), Arg.Any()); } [Theory, BitAutoData] diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CollectionRepositoryUpsertDefaultCollectionsTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CreateDefaultCollectionsTests.cs similarity index 94% rename from test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CollectionRepositoryUpsertDefaultCollectionsTests.cs rename to test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CreateDefaultCollectionsTests.cs index 54ae9cb19d..ee97655562 100644 --- a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CollectionRepositoryUpsertDefaultCollectionsTests.cs +++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/CreateDefaultCollectionsTests.cs @@ -10,10 +10,10 @@ using Xunit; namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.CollectionRepository; -public class CollectionRepositoryUpsertDefaultCollectionsTests +public class CreateDefaultCollectionsTests { /// - /// Test that UpsertDefaultCollectionsAsync successfully creates default collections for new users + /// Test that CreateDefaultCollectionsAsync successfully creates default collections for new users /// [DatabaseTheory, DatabaseData] public async Task UpsertDefaultCollectionsAsync_CreatesDefaultCollections_Success( @@ -62,7 +62,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests }); // Act - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, new[] { orgUser1.Id, orgUser2.Id }, "My Items"); @@ -77,7 +77,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests } /// - /// Test that calling UpsertDefaultCollectionsAsync multiple times does NOT create duplicates + /// Test that calling CreateDefaultCollectionsAsync multiple times does NOT create duplicates /// [DatabaseTheory, DatabaseData] public async Task UpsertDefaultCollectionsAsync_CalledMultipleTimes_DoesNotCreateDuplicates( @@ -111,14 +111,14 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests }); // Act - Call twice - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, new[] { orgUser.Id }, "My Items"); // Second call should not create duplicate await Assert.ThrowsAnyAsync(() => - collectionRepository.UpsertDefaultCollectionsAsync( + collectionRepository.CreateDefaultCollectionsAsync( organization.Id, new[] { orgUser.Id }, "My Items")); @@ -221,7 +221,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests Status = OrganizationUserStatusType.Confirmed, }); - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, new[] { orgUser.Id }, "My Items"); @@ -276,7 +276,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests Status = OrganizationUserStatusType.Confirmed, }); - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, new[] { orgUser.Id }, "My Items"); @@ -296,7 +296,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests } /// - /// Test that UpsertDefaultCollectionsAsync with empty user list does nothing + /// Test that CreateDefaultCollectionsAsync with empty user list does nothing /// [DatabaseTheory, DatabaseData] public async Task UpsertDefaultCollectionsAsync_WithEmptyList_DoesNothing( @@ -313,7 +313,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests }); // Act - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, Array.Empty(), "My Items"); @@ -324,7 +324,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests } /// - /// Test that UpsertDefaultCollectionsAsync creates CollectionUser entries with correct permissions + /// Test that CreateDefaultCollectionsAsync creates CollectionUser entries with correct permissions /// [DatabaseTheory, DatabaseData] public async Task UpsertDefaultCollectionsAsync_CreatesCollectionUsersWithCorrectPermissions( @@ -358,7 +358,7 @@ public class CollectionRepositoryUpsertDefaultCollectionsTests }); // Act - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, new[] { orgUser.Id }, "My Items"); diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs index 38dd28b367..234fa9ab35 100644 --- a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs +++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/DefaultCollectionSemaphoreTests.cs @@ -26,7 +26,7 @@ public class DefaultCollectionSemaphoreTests var organization = await organizationRepository.CreateTestOrganizationAsync(); var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, [orgUser.Id], "My Items"); @@ -59,7 +59,7 @@ public class DefaultCollectionSemaphoreTests var organization = await organizationRepository.CreateTestOrganizationAsync(); var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); - await collectionRepository.UpsertDefaultCollectionsAsync( + await collectionRepository.CreateDefaultCollectionsAsync( organization.Id, [orgUser.Id], "My Items");