diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index f1b3c9138f3..f9368414cb7 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -1534,9 +1534,6 @@
"file": {
"message": "File"
},
- "photo": {
- "message": "Photo"
- },
"unknown": {
"message": "Unknown"
},
@@ -4644,39 +4641,6 @@
"importPartialErrorDescription": {
"message": "The following items could not be read and will not be included in your import:"
},
- "contact": {
- "message": "Contact"
- },
- "membership": {
- "message": "Membership"
- },
- "passport": {
- "message": "Passport"
- },
- "driverLicense": {
- "message": "Driver license"
- },
- "ssnCard": {
- "message": "Identity card"
- },
- "birthCertificate": {
- "message": "Birth certificate"
- },
- "databaseCredentials": {
- "message": "Database credentials"
- },
- "serverCredentials": {
- "message": "Server credentials"
- },
- "softwareLicense": {
- "message": "Software license"
- },
- "healthInsurance": {
- "message": "Health insurance"
- },
- "wifiCredentials": {
- "message": "Wi-Fi credentials"
- },
"importEncKeyError": {
"message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data."
},
diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json
index 296c05a6f86..2ae4911875a 100644
--- a/apps/desktop/src/locales/en/messages.json
+++ b/apps/desktop/src/locales/en/messages.json
@@ -1019,9 +1019,6 @@
"file": {
"message": "File"
},
- "photo": {
- "message": "Photo"
- },
"selectFile": {
"message": "Select a file"
},
@@ -4021,42 +4018,6 @@
"importPartialErrorDescription": {
"message": "The following items could not be read and will not be included in your import:"
},
- "contact": {
- "message": "Contact"
- },
- "membership": {
- "message": "Membership"
- },
- "passport": {
- "message": "Passport"
- },
- "driverLicense": {
- "message": "Driver license"
- },
- "ssnCard": {
- "message": "Identity card"
- },
- "birthCertificate": {
- "message": "Birth certificate"
- },
- "databaseCredentials": {
- "message": "Database credentials"
- },
- "serverCredentials": {
- "message": "Server credentials"
- },
- "softwareLicense": {
- "message": "Software license"
- },
- "healthInsurance": {
- "message": "Health insurance"
- },
- "wifiCredentials": {
- "message": "Wi-Fi credentials"
- },
- "general": {
- "message": "General"
- },
"importEncKeyError": {
"message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data."
},
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index 8f37b4ad44d..798d87aae26 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -1570,9 +1570,6 @@
"file": {
"message": "File"
},
- "photo": {
- "message": "Photo"
- },
"selectFile": {
"message": "Select a file."
},
@@ -2770,42 +2767,6 @@
"importPartialErrorDescription": {
"message": "The following items could not be read and will not be included in your import:"
},
- "contact": {
- "message": "Contact"
- },
- "membership": {
- "message": "Membership"
- },
- "passport": {
- "message": "Passport"
- },
- "driverLicense": {
- "message": "Driver license"
- },
- "ssnCard": {
- "message": "Identity card"
- },
- "birthCertificate": {
- "message": "Birth certificate"
- },
- "databaseCredentials": {
- "message": "Database credentials"
- },
- "serverCredentials": {
- "message": "Server credentials"
- },
- "softwareLicense": {
- "message": "Software license"
- },
- "healthInsurance": {
- "message": "Health insurance"
- },
- "wifiCredentials": {
- "message": "Wi-Fi credentials"
- },
- "general": {
- "message": "General"
- },
"importEncKeyError": {
"message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data."
},
diff --git a/libs/importer/src/components/keeper/partial-import-dialog.component.html b/libs/importer/src/components/keeper/partial-import-dialog.component.html
index 9d0c8a7b832..451a1dc2af8 100644
--- a/libs/importer/src/components/keeper/partial-import-dialog.component.html
+++ b/libs/importer/src/components/keeper/partial-import-dialog.component.html
@@ -8,14 +8,12 @@
- | {{ "type" | i18n }} |
- {{ "items" | i18n }} |
+ {{ "name" | i18n }} |
- | {{ r.type }} |
- {{ r.count }} |
+ {{ r.name }} |
diff --git a/libs/importer/src/components/keeper/partial-import-dialog.component.ts b/libs/importer/src/components/keeper/partial-import-dialog.component.ts
index 777f0d2f984..1fdd8da0dba 100644
--- a/libs/importer/src/components/keeper/partial-import-dialog.component.ts
+++ b/libs/importer/src/components/keeper/partial-import-dialog.component.ts
@@ -12,10 +12,7 @@ import {
TableModule,
} from "@bitwarden/components";
-import {
- ImportRecordError,
- ImportRecordErrorReason,
-} from "../../importers/keeper/keeper-import-error";
+import { ImportRecordError } from "../../importers/keeper/keeper-import-error";
export interface PartialImportDialogData {
errors: ImportRecordError[];
@@ -23,35 +20,9 @@ export interface PartialImportDialogData {
}
interface SkippedItemRow {
- type: string;
- count: number;
+ name: string;
}
-// Localized label for each Keeper record type. Types not listed here (the pam* family and any custom
-// record types) fall back to the "Other" bucket.
-const KEEPER_TYPE_I18N_KEYS: Record = {
- login: "typeLogin",
- bankCard: "typeCard",
- bankAccount: "bankAccount",
- address: "address",
- contact: "contact",
- file: "file",
- photo: "photo",
- encryptedNotes: "typeSecureNote",
- ssnCard: "ssnCard",
- databaseCredentials: "databaseCredentials",
- serverCredentials: "serverCredentials",
- sshKeys: "typeSshKey",
- softwareLicense: "softwareLicense",
- healthInsurance: "healthInsurance",
- membership: "membership",
- passport: "passport",
- driverLicense: "driverLicense",
- birthCertificate: "birthCertificate",
- general: "general",
- wifiCredentials: "wifiCredentials",
-};
-
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
@@ -68,7 +39,9 @@ export class PartialImportDialogComponent implements OnInit {
) {}
ngOnInit(): void {
- this.dataSource.data = this.buildRows(this.data.errors);
+ // Items that could not be read have no decrypted title, so they show as "Unknown".
+ const unknown = this.i18nService.t("unknown");
+ this.dataSource.data = this.data.errors.map((error) => ({ name: error.name || unknown }));
}
protected continueImport(): void {
@@ -78,24 +51,4 @@ export class PartialImportDialogComponent implements OnInit {
protected cancel(): void {
void this.dialogRef.close(false);
}
-
- private buildRows(errors: ImportRecordError[]): SkippedItemRow[] {
- const counts = new Map();
- for (const error of errors) {
- const type = this.typeLabel(error);
- counts.set(type, (counts.get(type) ?? 0) + 1);
- }
- return Array.from(counts, ([type, count]) => ({ type, count }));
- }
-
- private typeLabel(error: ImportRecordError): string {
- // Folders that could not be decrypted are reported as their own bucket.
- if (error.reason === ImportRecordErrorReason.FolderDecryptionFailed) {
- return this.i18nService.t("folder");
- }
-
- // Known Keeper types get their label; unmapped types and unreadable items fall back to "Other".
- const mapped = error.type ? KEEPER_TYPE_I18N_KEYS[error.type] : undefined;
- return this.i18nService.t(mapped ?? "other");
- }
}
diff --git a/libs/importer/src/importers/keeper/keeper-direct-importer.spec.ts b/libs/importer/src/importers/keeper/keeper-direct-importer.spec.ts
index 215718bd0d3..382dbe7aaec 100644
--- a/libs/importer/src/importers/keeper/keeper-direct-importer.spec.ts
+++ b/libs/importer/src/importers/keeper/keeper-direct-importer.spec.ts
@@ -191,11 +191,7 @@ describe("Keeper Direct Importer", () => {
it("should not import file records and report them as unsupported", () => {
expect(result.ciphers.find((c) => c.name === "Project Proposal Document")).toBeUndefined();
expect(errors).toContainEqual(
- new ImportRecordError(
- "Project Proposal Document",
- ImportRecordErrorReason.UnsupportedType,
- "file",
- ),
+ new ImportRecordError("Project Proposal Document", ImportRecordErrorReason.UnsupportedType),
);
});
@@ -343,11 +339,7 @@ describe("Keeper Direct Importer", () => {
it("should not import photo records and report them as unsupported", () => {
expect(result.ciphers.find((c) => c.name === "Family Vacation 2024")).toBeUndefined();
expect(errors).toContainEqual(
- new ImportRecordError(
- "Family Vacation 2024",
- ImportRecordErrorReason.UnsupportedType,
- "photo",
- ),
+ new ImportRecordError("Family Vacation 2024", ImportRecordErrorReason.UnsupportedType),
);
});
@@ -579,24 +571,24 @@ describe("Keeper Direct Importer error handling", () => {
return new KeeperDirectImporter().convertVaultToImportResult(vault);
}
- it("maps an UnsupportedVersion vault error to UnsupportedFeature with the UID as name", () => {
+ it("maps an UnsupportedVersion vault error to UnsupportedFeature with no readable name", () => {
const { errors } = importWith(
[],
[{ id: "uid-1", reason: VaultRecordErrorReason.UnsupportedVersion }],
);
expect(errors).toContainEqual(
- new ImportRecordError("uid-1", ImportRecordErrorReason.UnsupportedFeature),
+ new ImportRecordError("", ImportRecordErrorReason.UnsupportedFeature),
);
});
- it("maps a DecryptionFailed vault error to a generic error with the UID as name", () => {
+ it("maps a DecryptionFailed vault error to a generic error with no readable name", () => {
const { errors } = importWith(
[],
[{ id: "uid-2", reason: VaultRecordErrorReason.DecryptionFailed }],
);
- expect(errors).toContainEqual(new ImportRecordError("uid-2", ImportRecordErrorReason.Error));
+ expect(errors).toContainEqual(new ImportRecordError("", ImportRecordErrorReason.Error));
});
it("maps a FolderDecryptionFailed vault error and still imports the affected record at the root", () => {
@@ -608,7 +600,7 @@ describe("Keeper Direct Importer error handling", () => {
);
expect(errors).toContainEqual(
- new ImportRecordError("folder-uid", ImportRecordErrorReason.FolderDecryptionFailed),
+ new ImportRecordError("", ImportRecordErrorReason.FolderDecryptionFailed),
);
const cipher = result.ciphers.find((c) => c.name === "Rootless Record");
@@ -635,7 +627,7 @@ describe("Keeper Direct Importer error handling", () => {
// The throwing record produces exactly one generic error.
expect(errors).toContainEqual(
- new ImportRecordError("Throwing Record", ImportRecordErrorReason.Error, "login"),
+ new ImportRecordError("Throwing Record", ImportRecordErrorReason.Error),
);
expect(errors.filter((e) => e.reason === ImportRecordErrorReason.Error).length).toBe(1);
diff --git a/libs/importer/src/importers/keeper/keeper-direct-importer.ts b/libs/importer/src/importers/keeper/keeper-direct-importer.ts
index 38f327390f9..b5618a4757d 100644
--- a/libs/importer/src/importers/keeper/keeper-direct-importer.ts
+++ b/libs/importer/src/importers/keeper/keeper-direct-importer.ts
@@ -55,8 +55,8 @@ export class KeeperDirectImporter extends BaseImporter {
private mapVaultErrors(vaultErrors: VaultRecordError[], errors: ImportRecordError[]): void {
for (const error of vaultErrors) {
- // The record/folder name was never decrypted, so the UID is the only identifier we have.
- errors.push(new ImportRecordError(error.id, mapVaultErrorReason(error.reason)));
+ // The record/folder name was never decrypted, so there is no name to show.
+ errors.push(new ImportRecordError("", mapVaultErrorReason(error.reason)));
}
}
@@ -67,22 +67,14 @@ export class KeeperDirectImporter extends BaseImporter {
): void {
for (const item of items) {
if (UNSUPPORTED_RECORD_TYPES.has(item.type)) {
- errors.push(
- new ImportRecordError(
- item.title || item.id,
- ImportRecordErrorReason.UnsupportedType,
- item.type,
- ),
- );
+ errors.push(new ImportRecordError(item.title, ImportRecordErrorReason.UnsupportedType));
continue;
}
try {
this.parseRecord(item, result);
} catch {
- errors.push(
- new ImportRecordError(item.title || item.id, ImportRecordErrorReason.Error, item.type),
- );
+ errors.push(new ImportRecordError(item.title, ImportRecordErrorReason.Error));
}
}
}
diff --git a/libs/importer/src/importers/keeper/keeper-import-error.ts b/libs/importer/src/importers/keeper/keeper-import-error.ts
index 5e1be85910d..18890d45131 100644
--- a/libs/importer/src/importers/keeper/keeper-import-error.ts
+++ b/libs/importer/src/importers/keeper/keeper-import-error.ts
@@ -9,10 +9,8 @@ export type ImportRecordErrorReason =
export class ImportRecordError {
constructor(
+ // The item title, or empty when the item could not be read and its name is unknown.
readonly name: string,
readonly reason: ImportRecordErrorReason,
- // Raw Keeper record type (e.g. "login", "bankCard", "file"). Undefined when the item could not
- // be read, so its type is unknown.
- readonly type?: string,
) {}
}