[PM-37175] Preserve individual vault export folders when importing to My Items (#20883)

This commit is contained in:
Mike Amirault 2026-05-29 13:54:58 -04:00 committed by GitHub
parent 42b0a4dff2
commit eec6bc1a31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 21 deletions

View File

@ -297,7 +297,31 @@ describe("ImportService", () => {
await expect(setImportTargetMethod).rejects.toThrow();
});
it("If importTarget is of type DefaultUserCollection create any collections as folders", async () => {
it("If importTarget is of type DefaultUserCollection and import has folders, preserve them", async () => {
importResult.folders.push(mockFolder1);
importResult.folders.push(mockFolder2);
importResult.ciphers.push(createCipher({ name: "cipher1", folderId: mockFolder1.id }));
importResult.ciphers.push(createCipher({ name: "cipher2", folderId: mockFolder2.id }));
mockImportTargetCollection.type = CollectionTypes.DefaultUserCollection;
await importService["setImportTarget"](
importResult,
organizationId,
mockImportTargetCollection,
);
expect(importResult.folders.length).toEqual(2);
expect(importResult.folders[0].name).toEqual(mockFolder1.name);
expect(importResult.folders[1].name).toEqual(mockFolder2.name);
expect(importResult.folderRelationships.length).toEqual(2);
expect(importResult.folderRelationships[0]).toEqual([0, 0]);
expect(importResult.folderRelationships[1]).toEqual([1, 1]);
expect(importResult.collectionRelationships.length).toEqual(2);
expect(importResult.collectionRelationships[0]).toEqual([0, 0]);
expect(importResult.collectionRelationships[1]).toEqual([1, 0]);
});
it("If importTarget is of type DefaultUserCollection and import has no folders, convert collections to folders", async () => {
importResult.collections.push(mockCollection1);
importResult.collections.push(mockCollection2);
importResult.ciphers.push(

View File

@ -524,27 +524,38 @@ export class ImportService implements ImportServiceAbstraction {
});
if (importTarget.type === CollectionTypes.DefaultUserCollection) {
// Since ciphers can only have one folder (for now)
// we bail if any are a part of multiple Collections
if (
importResult.ciphers.some(
(_c, c_idx) =>
importResult.collectionRelationships.filter((cr) => cr[0] === c_idx).length > 1,
)
) {
throw new Error(this.i18nService.t("errorImportingMyItemsMultiCollection"));
// For individual vault export files we preserve any existing folders
if (importResult.folders.length > 0) {
for (let i = 0; i < importResult.ciphers.length; i++) {
const cipherFolderIndex = importResult.folders.findIndex(
(f) => f.id === importResult.ciphers[i].folderId,
);
if (cipherFolderIndex !== -1) {
importResult.folderRelationships.push([i, cipherFolderIndex]);
}
}
// For organization vault export files we turn any collections into folders.
// Ciphers can only have one folder (for now) so bail if any have multiple collections
} else {
if (
importResult.ciphers.some(
(_c, c_idx) =>
importResult.collectionRelationships.filter((cr) => cr[0] === c_idx).length > 1,
)
) {
throw new Error(this.i18nService.t("errorImportingMyItemsMultiCollection"));
}
importResult.folders = importResult.collections.map((c) => {
const f = new FolderView();
f.name = c.name;
return f;
});
importResult.folderRelationships = importResult.collectionRelationships.map((c) => [
c[0],
c[1],
]);
}
importResult.folders = importResult.collections.map((c) => {
const f = new FolderView();
f.name = c.name;
return f;
});
// We use the collection relationships to create the folder relationships
importResult.folderRelationships = importResult.collectionRelationships.map((c) => [
c[0],
c[1],
]);
// We then set the target collection to My Items...
// In either case set target collection to My Items...
importResult.collections = [importTarget];
// ...and set the collection relationships accordingly
importResult.collectionRelationships = importResult.ciphers.map((_c, idx) => [idx, 0]);