diff --git a/libs/common/spec/services/export.service.spec.ts b/libs/common/spec/services/export.service.spec.ts index 19177bc171..92dbd16883 100644 --- a/libs/common/spec/services/export.service.spec.ts +++ b/libs/common/spec/services/export.service.spec.ts @@ -1,6 +1,5 @@ // eslint-disable-next-line no-restricted-imports import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; -import { BehaviorSubject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; @@ -12,9 +11,11 @@ import { KdfType } from "@bitwarden/common/enums/kdfType"; import { Utils } from "@bitwarden/common/misc/utils"; import { Cipher } from "@bitwarden/common/models/domain/cipher"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; +import { Folder } from "@bitwarden/common/models/domain/folder"; import { Login } from "@bitwarden/common/models/domain/login"; import { CipherWithIdExport as CipherExport } from "@bitwarden/common/models/export/cipher-with-ids.export"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; +import { FolderView } from "@bitwarden/common/models/view/folder.view"; import { LoginView } from "@bitwarden/common/models/view/login.view"; import { ExportService } from "@bitwarden/common/services/export.service"; @@ -32,6 +33,10 @@ const UserCipherDomains = [ generateCipherDomain(true), ]; +const UserFolderViews = [generateFolderView(), generateFolderView()]; + +const UserFolders = [generateFolder(), generateFolder()]; + function generateCipherView(deleted: boolean) { return BuildTestObject( { @@ -72,6 +77,26 @@ function generateCipherDomain(deleted: boolean) { ); } +function generateFolderView() { + return BuildTestObject( + { + id: GetUniqueString("id"), + name: GetUniqueString("name"), + revisionDate: new Date(), + }, + FolderView + ); +} + +function generateFolder() { + const actual = Folder.fromJSON({ + revisionDate: new Date("2022-08-04T01:06:40.441Z").toISOString(), + name: "name", + id: "id", + }); + return actual; +} + function expectEqualCiphers(ciphers: CipherView[] | Cipher[], jsonResult: string) { const actual = JSON.stringify(JSON.parse(jsonResult).items); const items: CipherExport[] = []; @@ -84,6 +109,34 @@ function expectEqualCiphers(ciphers: CipherView[] | Cipher[], jsonResult: string expect(actual).toEqual(JSON.stringify(items)); } +function expectEqualFolderViews(folderviews: FolderView[] | Folder[], jsonResult: string) { + const actual = JSON.stringify(JSON.parse(jsonResult).folders); + const folders: FolderResponse[] = []; + folderviews.forEach((c) => { + const folder = new FolderResponse(); + folder.id = c.id; + folder.name = c.name.toString(); + folders.push(folder); + }); + + expect(actual.length).toBeGreaterThan(0); + expect(actual).toEqual(JSON.stringify(folders)); +} + +function expectEqualFolders(folders: Folder[], jsonResult: string) { + const actual = JSON.stringify(JSON.parse(jsonResult).folders); + const items: Folder[] = []; + folders.forEach((c) => { + const item = new Folder(); + item.id = c.id; + item.name = c.name; + items.push(item); + }); + + expect(actual.length).toBeGreaterThan(0); + expect(actual).toEqual(JSON.stringify(items)); +} + describe("ExportService", () => { let exportService: ExportService; let apiService: SubstituteOf; @@ -99,8 +152,8 @@ describe("ExportService", () => { folderService = Substitute.for(); cryptoService = Substitute.for(); - folderService.folderViews$.returns(new BehaviorSubject([])); - folderService.folders$.returns(new BehaviorSubject([])); + folderService.getAllDecryptedFromState().resolves(UserFolderViews); + folderService.getAllFromState().resolves(UserFolders); exportService = new ExportService( folderService, @@ -208,4 +261,25 @@ describe("ExportService", () => { }); }); }); + + it("exported unencrypted object contains folders", async () => { + cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1)); + await folderService.getAllDecryptedFromState(); + const actual = await exportService.getExport("json"); + + expectEqualFolderViews(UserFolderViews, actual); + }); + + it("exported encrypted json contains folders", async () => { + cipherService.getAll().resolves(UserCipherDomains.slice(0, 1)); + await folderService.getAllFromState(); + const actual = await exportService.getExport("encrypted_json"); + + expectEqualFolders(UserFolders, actual); + }); }); + +export class FolderResponse { + id: string = null; + name: string = null; +} diff --git a/libs/common/src/abstractions/folder/folder.service.abstraction.ts b/libs/common/src/abstractions/folder/folder.service.abstraction.ts index 44db4a985b..90b6fc2332 100644 --- a/libs/common/src/abstractions/folder/folder.service.abstraction.ts +++ b/libs/common/src/abstractions/folder/folder.service.abstraction.ts @@ -12,6 +12,7 @@ export abstract class FolderService { clearCache: () => Promise; encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise; get: (id: string) => Promise; + getAllFromState: () => Promise; /** * @deprecated Only use in CLI! */ diff --git a/libs/common/src/services/export.service.ts b/libs/common/src/services/export.service.ts index 4c2f51026a..690701b83e 100644 --- a/libs/common/src/services/export.service.ts +++ b/libs/common/src/services/export.service.ts @@ -1,5 +1,4 @@ import * as papa from "papaparse"; -import { firstValueFrom } from "rxjs"; import { ApiService } from "../abstractions/api.service"; import { CipherService } from "../abstractions/cipher.service"; @@ -116,7 +115,7 @@ export class ExportService implements ExportServiceAbstraction { const promises = []; promises.push( - firstValueFrom(this.folderService.folderViews$).then((folders) => { + this.folderService.getAllDecryptedFromState().then((folders) => { decFolders = folders; }) ); @@ -192,7 +191,7 @@ export class ExportService implements ExportServiceAbstraction { const promises = []; promises.push( - firstValueFrom(this.folderService.folders$).then((f) => { + this.folderService.getAllFromState().then((f) => { folders = f; }) ); diff --git a/libs/common/src/services/folder/folder.service.ts b/libs/common/src/services/folder/folder.service.ts index 096d89cea9..85ef564fd5 100644 --- a/libs/common/src/services/folder/folder.service.ts +++ b/libs/common/src/services/folder/folder.service.ts @@ -64,6 +64,18 @@ export class FolderService implements InternalFolderServiceAbstraction { return folders.find((folder) => folder.id === id); } + async getAllFromState(): Promise { + const folders = await this.stateService.getEncryptedFolders(); + const response: Folder[] = []; + for (const id in folders) { + // eslint-disable-next-line + if (folders.hasOwnProperty(id)) { + response.push(new Folder(folders[id])); + } + } + return response; + } + /** * @deprecated For the CLI only * @param id id of the folder