Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8d67715
Fix UpdateCollectionCommand to set RevisionDate using TimeProvider an…
r-tome Apr 2, 2026
a330cd6
Enhance BulkAddCollectionAccessCommand to include revision date in ac…
r-tome Apr 3, 2026
59a90c6
Update GroupRepository and stored procedures to bump RevisionDate for…
r-tome Apr 3, 2026
4ee23dd
Implement revision date updates for affected collections in Organizat…
r-tome Apr 3, 2026
0a7c65f
Update database migration script
r-tome Apr 3, 2026
e878ae2
Update migration script summary
r-tome Apr 3, 2026
48d9e7c
Refactor OrganizationUserReplaceTests to create collection first
r-tome Apr 3, 2026
2cc7800
Merge branch 'main' into ac/pm-22450/collection-revision-date-is-not-…
r-tome Apr 4, 2026
2aafad2
Refactor stored procedures to use Common Table Expressions (CTEs) for…
r-tome Apr 6, 2026
ab54a70
Enhance OrganizationUser_CreateManyWithCollectionsAndGroups stored pr…
r-tome Apr 6, 2026
4d80849
Refactor OrganizationUser_CreateManyWithCollectionsGroups and migrati…
r-tome Apr 6, 2026
7206101
Refactor OrganizationUserRepository to consistently use RevisionDate …
r-tome Apr 6, 2026
1f9ae7b
Refactor tests to ensure consistent handling of RevisionDate across G…
r-tome Apr 6, 2026
0e81b49
Restore BOM in Group_UpdateWithCollections and OrganizationUser_Updat…
r-tome Apr 6, 2026
1bd19cb
Refactor GroupRepository and OrganizationUserRepository to improve ha…
r-tome Apr 6, 2026
dabb672
Bump migration script date
r-tome Apr 6, 2026
4948c15
Remove internal set from RevisionDate on Group and OrganizationUser
r-tome Apr 6, 2026
7ae69c9
Refactor OrganizationUser_CreateManyWithCollectionsGroups and migrati…
r-tome Apr 6, 2026
7b39459
Merge branch 'main' into ac/pm-22450/collection-revision-date-is-not-…
r-tome Apr 7, 2026
c3cf999
Fix sprocs styling
r-tome Apr 7, 2026
7de27a9
Added early return to OrganizationUserRepository.CreateManyAsync if t…
r-tome Apr 7, 2026
a308ef2
Merge branch 'main' into ac/pm-22450/collection-revision-date-is-not-…
r-tome Apr 8, 2026
d339457
Merge branch 'main' into ac/pm-22450/collection-revision-date-is-not-…
r-tome Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ public class UpdateCollectionCommand : IUpdateCollectionCommand
private readonly IEventService _eventService;
private readonly IOrganizationRepository _organizationRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly TimeProvider _timeProvider;

public UpdateCollectionCommand(
IEventService eventService,
IOrganizationRepository organizationRepository,
ICollectionRepository collectionRepository)
ICollectionRepository collectionRepository,
TimeProvider timeProvider)
{
_eventService = eventService;
_organizationRepository = organizationRepository;
_collectionRepository = collectionRepository;
_timeProvider = timeProvider;
}

public async Task<Collection> UpdateAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null,
Expand Down Expand Up @@ -75,6 +78,7 @@ public async Task<Collection> UpdateAsync(Collection collection, IEnumerable<Col
}
}

collection.RevisionDate = _timeProvider.GetUtcNow().UtcDateTime;
await _collectionRepository.ReplaceAsync(collection, org.UseGroups ? groupsList : null, usersList);
await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Updated);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.Extensions.Time.Testing;
using NSubstitute;
using Xunit;

Expand All @@ -19,12 +20,16 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationCollections;
[OrganizationCustomize]
public class UpdateCollectionCommandTests
{
private static readonly DateTime _expectedRevisionDate = DateTime.UtcNow.AddYears(1);

[Theory, BitAutoData]
public async Task UpdateAsync_WithoutGroupsAndUsers_ReplacesCollection(
Organization organization, Collection collection,
[CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> existingUsers,
SutProvider<UpdateCollectionCommand> sutProvider)
[CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> existingUsers)
{
var sutProvider = SetupSutProvider();
collection.RevisionDate = DateTime.UtcNow.AddYears(-1);

organization.AllowAdminAccessToAllCollectionItems = false;
var creationDate = collection.CreationDate;
sutProvider.GetDependency<IOrganizationRepository>()
Expand All @@ -35,7 +40,6 @@ public async Task UpdateAsync_WithoutGroupsAndUsers_ReplacesCollection(
.Returns(new Tuple<Collection?, CollectionAccessDetails>(
collection,
new CollectionAccessDetails { Groups = [], Users = existingUsers }));
var utcNow = DateTime.UtcNow;

await sutProvider.Sut.UpdateAsync(collection, null, null);

Expand All @@ -49,22 +53,23 @@ await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogCollectionEventAsync(collection, EventType.Collection_Updated);
Assert.Equal(collection.CreationDate, creationDate);
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
Assert.Equal(_expectedRevisionDate, collection.RevisionDate);
}

[Theory, BitAutoData]
public async Task UpdateAsync_WithGroupsAndUsers_ReplacesCollectionWithGroupsAndUsers(
Organization organization, Collection collection,
[CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> groups,
IEnumerable<CollectionAccessSelection> users,
SutProvider<UpdateCollectionCommand> sutProvider)
IEnumerable<CollectionAccessSelection> users)
{
var sutProvider = SetupSutProvider();
collection.RevisionDate = DateTime.UtcNow.AddYears(-1);

var creationDate = collection.CreationDate;
organization.UseGroups = true;
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
var utcNow = DateTime.UtcNow;

await sutProvider.Sut.UpdateAsync(collection, groups, users);

Expand All @@ -78,22 +83,23 @@ await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogCollectionEventAsync(collection, EventType.Collection_Updated);
Assert.Equal(collection.CreationDate, creationDate);
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
Assert.Equal(_expectedRevisionDate, collection.RevisionDate);
}

[Theory, BitAutoData]
public async Task UpdateAsync_WithOrganizationUseGroupDisabled_ReplacesCollectionWithoutGroups(
Organization organization, Collection collection,
[CollectionAccessSelectionCustomize] IEnumerable<CollectionAccessSelection> groups,
[CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> users,
SutProvider<UpdateCollectionCommand> sutProvider)
[CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> users)
{
var sutProvider = SetupSutProvider();
collection.RevisionDate = DateTime.UtcNow.AddYears(-1);

var creationDate = collection.CreationDate;
organization.UseGroups = false;
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
var utcNow = DateTime.UtcNow;

await sutProvider.Sut.UpdateAsync(collection, groups, users);

Expand All @@ -107,7 +113,7 @@ await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogCollectionEventAsync(collection, EventType.Collection_Updated);
Assert.Equal(collection.CreationDate, creationDate);
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
Assert.Equal(_expectedRevisionDate, collection.RevisionDate);
}

[Theory, BitAutoData]
Expand Down Expand Up @@ -316,4 +322,13 @@ await sutProvider.GetDependency<ICollectionRepository>()
.Received(1)
.ReplaceAsync(collection, null, null);
}

private static SutProvider<UpdateCollectionCommand> SetupSutProvider()
{
var sutProvider = new SutProvider<UpdateCollectionCommand>()
.WithFakeTimeProvider()
.Create();
sutProvider.GetDependency<FakeTimeProvider>().SetUtcNow(_expectedRevisionDate);
return sutProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ await collectionRepository.CreateAsync(collection,
);

// Act
var originalRevisionDate = collection.RevisionDate;
collection.Name = "Updated Collection Name";
collection.RevisionDate = DateTime.UtcNow;

await collectionRepository.ReplaceAsync(collection,
[
Expand All @@ -74,6 +76,7 @@ await collectionRepository.ReplaceAsync(collection,

Assert.NotNull(actualCollection);
Assert.Equal("Updated Collection Name", actualCollection.Name);
Assert.True(actualCollection.RevisionDate > originalRevisionDate);

var groups = actualAccess.Groups.ToArray();
Assert.Equal(2, groups.Length);
Expand Down
Loading