1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 07:36:14 -05:00

[PM-22974] Cascade delete NotificationStatus entities (#6011)

* cascade delete NotificationStatus entities

* add userId to test for foreign constraint

* add missing properties for Notification

* add check for foreign key
This commit is contained in:
Nick Krantz
2025-06-27 11:17:47 -05:00
committed by GitHub
parent 290fa3ded4
commit 8bccf255c0
5 changed files with 45 additions and 3 deletions

View File

@ -13,6 +13,12 @@ public class NotificationStatusEntityTypeConfiguration : IEntityTypeConfiguratio
.HasKey(ns => new { ns.UserId, ns.NotificationId }) .HasKey(ns => new { ns.UserId, ns.NotificationId })
.IsClustered(); .IsClustered();
builder
.HasOne(ns => ns.Notification)
.WithMany()
.HasForeignKey(ns => ns.NotificationId)
.OnDelete(DeleteBehavior.Cascade);
builder.ToTable(nameof(NotificationStatus)); builder.ToTable(nameof(NotificationStatus));
} }
} }

View File

@ -5,11 +5,10 @@ CREATE TABLE [dbo].[NotificationStatus]
[ReadDate] DATETIME2 (7) NULL, [ReadDate] DATETIME2 (7) NULL,
[DeletedDate] DATETIME2 (7) NULL, [DeletedDate] DATETIME2 (7) NULL,
CONSTRAINT [PK_NotificationStatus] PRIMARY KEY CLUSTERED ([NotificationId] ASC, [UserId] ASC), CONSTRAINT [PK_NotificationStatus] PRIMARY KEY CLUSTERED ([NotificationId] ASC, [UserId] ASC),
CONSTRAINT [FK_NotificationStatus_Notification] FOREIGN KEY ([NotificationId]) REFERENCES [dbo].[Notification] ([Id]), CONSTRAINT [FK_NotificationStatus_Notification] FOREIGN KEY ([NotificationId]) REFERENCES [dbo].[Notification] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_NotificationStatus_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) CONSTRAINT [FK_NotificationStatus_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
); );
GO GO
CREATE NONCLUSTERED INDEX [IX_NotificationStatus_UserId] CREATE NONCLUSTERED INDEX [IX_NotificationStatus_UserId]
ON [dbo].[NotificationStatus]([UserId] ASC); ON [dbo].[NotificationStatus]([UserId] ASC);

View File

@ -5,6 +5,8 @@ using Bit.Core.Billing.Enums;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.NotificationCenter.Entities;
using Bit.Core.NotificationCenter.Repositories;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Vault.Entities; using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Enums; using Bit.Core.Vault.Enums;
@ -976,8 +978,11 @@ public class CipherRepositoryTests
[DatabaseTheory, DatabaseData] [DatabaseTheory, DatabaseData]
public async Task DeleteCipherWithSecurityTaskAsync_Works( public async Task DeleteCipherWithSecurityTaskAsync_Works(
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
IUserRepository userRepository,
ICipherRepository cipherRepository, ICipherRepository cipherRepository,
ISecurityTaskRepository securityTaskRepository) ISecurityTaskRepository securityTaskRepository,
INotificationRepository notificationRepository,
INotificationStatusRepository notificationStatusRepository)
{ {
var organization = await organizationRepository.CreateAsync(new Organization var organization = await organizationRepository.CreateAsync(new Organization
{ {
@ -987,6 +992,14 @@ public class CipherRepositoryTests
BillingEmail = "" BillingEmail = ""
}); });
var user = await userRepository.CreateAsync(new User
{
Name = "Test User",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
var cipher1 = new Cipher { Type = CipherType.Login, OrganizationId = organization.Id, Data = "", }; var cipher1 = new Cipher { Type = CipherType.Login, OrganizationId = organization.Id, Data = "", };
await cipherRepository.CreateAsync(cipher1); await cipherRepository.CreateAsync(cipher1);
@ -1012,6 +1025,20 @@ public class CipherRepositoryTests
}; };
await securityTaskRepository.CreateManyAsync(tasks); await securityTaskRepository.CreateManyAsync(tasks);
var notification = await notificationRepository.CreateAsync(new Notification
{
OrganizationId = organization.Id,
UserId = user.Id,
TaskId = tasks[1].Id,
CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow,
});
await notificationStatusRepository.CreateAsync(new NotificationStatus
{
NotificationId = notification.Id,
UserId = user.Id,
ReadDate = DateTime.UtcNow,
});
// Delete cipher with pending security task // Delete cipher with pending security task
await cipherRepository.DeleteAsync(cipher1); await cipherRepository.DeleteAsync(cipher1);

View File

@ -0,0 +1,10 @@
BEGIN
IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_NotificationStatus_Notification')
BEGIN
ALTER TABLE [dbo].[NotificationStatus] DROP CONSTRAINT [FK_NotificationStatus_Notification]
END
ALTER TABLE [dbo].[NotificationStatus]
ADD CONSTRAINT [FK_NotificationStatus_Notification] FOREIGN KEY ([NotificationId]) REFERENCES [dbo].[Notification]([Id]) ON DELETE CASCADE
END
GO