From 3beeab4414e8c1fe21186a07eaa3ce881abf4588 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Mon, 29 Dec 2025 13:49:00 -0500 Subject: [PATCH] [PM-29972] Update Vault Items List When Archiving Ciphers (#18102) * update default cipher service to use upsert, apply optional userId parameter --- .../src/vault/abstractions/cipher.service.ts | 6 +++++- .../src/vault/services/cipher.service.ts | 7 +++++-- .../default-cipher-archive.service.spec.ts | 20 +++++++++---------- .../default-cipher-archive.service.ts | 4 ++-- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/libs/common/src/vault/abstractions/cipher.service.ts b/libs/common/src/vault/abstractions/cipher.service.ts index 8472a359c51..0d3a0b99fcb 100644 --- a/libs/common/src/vault/abstractions/cipher.service.ts +++ b/libs/common/src/vault/abstractions/cipher.service.ts @@ -207,9 +207,13 @@ export abstract class CipherService implements UserKeyRotationDataProvider>; + abstract upsert( + cipher: CipherData | CipherData[], + userId?: UserId, + ): Promise>; abstract replace(ciphers: { [id: string]: CipherData }, userId: UserId): Promise; abstract clear(userId?: string): Promise; abstract moveManyWithServer(ids: string[], folderId: string, userId: UserId): Promise; diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 402b8ed1030..3c44b854de7 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -1196,12 +1196,15 @@ export class CipherService implements CipherServiceAbstraction { await this.encryptedCiphersState(userId).update(() => ciphers); } - async upsert(cipher: CipherData | CipherData[]): Promise> { + async upsert( + cipher: CipherData | CipherData[], + userId?: UserId, + ): Promise> { const ciphers = cipher instanceof CipherData ? [cipher] : cipher; const res = await this.updateEncryptedCipherState((current) => { ciphers.forEach((c) => (current[c.id as CipherId] = c)); return current; - }); + }, userId); // Some state storage providers (e.g. Electron) don't update the state immediately, wait for next tick // Otherwise, subscribers to cipherViews$ can get stale data await new Promise((resolve) => setTimeout(resolve, 0)); diff --git a/libs/common/src/vault/services/default-cipher-archive.service.spec.ts b/libs/common/src/vault/services/default-cipher-archive.service.spec.ts index 807311ca851..2f5e69d65ed 100644 --- a/libs/common/src/vault/services/default-cipher-archive.service.spec.ts +++ b/libs/common/src/vault/services/default-cipher-archive.service.spec.ts @@ -219,7 +219,7 @@ describe("DefaultCipherArchiveService", () => { } as any, }), ); - mockCipherService.replace.mockResolvedValue(undefined); + mockCipherService.upsert.mockResolvedValue(undefined); }); it("should archive single cipher", async () => { @@ -233,13 +233,13 @@ describe("DefaultCipherArchiveService", () => { true, ); expect(mockCipherService.ciphers$).toHaveBeenCalledWith(userId); - expect(mockCipherService.replace).toHaveBeenCalledWith( - expect.objectContaining({ - [cipherId]: expect.objectContaining({ + expect(mockCipherService.upsert).toHaveBeenCalledWith( + [ + expect.objectContaining({ archivedDate: "2024-01-15T10:30:00.000Z", revisionDate: "2024-01-15T10:31:00.000Z", }), - }), + ], userId, ); }); @@ -282,7 +282,7 @@ describe("DefaultCipherArchiveService", () => { } as any, }), ); - mockCipherService.replace.mockResolvedValue(undefined); + mockCipherService.upsert.mockResolvedValue(undefined); }); it("should unarchive single cipher", async () => { @@ -296,12 +296,12 @@ describe("DefaultCipherArchiveService", () => { true, ); expect(mockCipherService.ciphers$).toHaveBeenCalledWith(userId); - expect(mockCipherService.replace).toHaveBeenCalledWith( - expect.objectContaining({ - [cipherId]: expect.objectContaining({ + expect(mockCipherService.upsert).toHaveBeenCalledWith( + [ + expect.objectContaining({ revisionDate: "2024-01-15T10:31:00.000Z", }), - }), + ], userId, ); }); diff --git a/libs/common/src/vault/services/default-cipher-archive.service.ts b/libs/common/src/vault/services/default-cipher-archive.service.ts index 8076735c9e2..c1daade0dad 100644 --- a/libs/common/src/vault/services/default-cipher-archive.service.ts +++ b/libs/common/src/vault/services/default-cipher-archive.service.ts @@ -95,7 +95,7 @@ export class DefaultCipherArchiveService implements CipherArchiveService { localCipher.revisionDate = cipher.revisionDate; } - await this.cipherService.replace(currentCiphers, userId); + await this.cipherService.upsert(Object.values(currentCiphers), userId); } async unarchiveWithServer(ids: CipherId | CipherId[], userId: UserId): Promise { @@ -116,6 +116,6 @@ export class DefaultCipherArchiveService implements CipherArchiveService { localCipher.revisionDate = cipher.revisionDate; } - await this.cipherService.replace(currentCiphers, userId); + await this.cipherService.upsert(Object.values(currentCiphers), userId); } }