mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 08:43:33 +00:00
Elevate Map <-> Record JSON helpers to Utils
This commit is contained in:
@@ -241,4 +241,59 @@ describe("Utils Service", () => {
|
|||||||
expect(Utils.fromByteStringToArray(null)).toEqual(null);
|
expect(Utils.fromByteStringToArray(null)).toEqual(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("mapToRecord", () => {
|
||||||
|
it("should handle null", () => {
|
||||||
|
expect(Utils.mapToRecord(null)).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle empty map", () => {
|
||||||
|
expect(Utils.mapToRecord(new Map())).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle convert a Map to a Record", () => {
|
||||||
|
const map = new Map([
|
||||||
|
["key1", "value1"],
|
||||||
|
["key2", "value2"],
|
||||||
|
]);
|
||||||
|
expect(Utils.mapToRecord(map)).toEqual({ key1: "value1", key2: "value2" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle convert a Map to a Record with non-string keys", () => {
|
||||||
|
const map = new Map([
|
||||||
|
[1, "value1"],
|
||||||
|
[2, "value2"],
|
||||||
|
]);
|
||||||
|
const result = Utils.mapToRecord(map);
|
||||||
|
expect(result).toEqual({ 1: "value1", 2: "value2" });
|
||||||
|
expect(Utils.recordToMap(result)).toEqual(map);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("recordToMap", () => {
|
||||||
|
it("should handle null", () => {
|
||||||
|
expect(Utils.recordToMap(null)).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle empty record", () => {
|
||||||
|
expect(Utils.recordToMap({})).toEqual(new Map());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle convert a Record to a Map", () => {
|
||||||
|
const record = { key1: "value1", key2: "value2" };
|
||||||
|
expect(Utils.recordToMap(record)).toEqual(new Map(Object.entries(record)));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle convert a Record to a Map with non-string keys", () => {
|
||||||
|
const record = { 1: "value1", 2: "value2" };
|
||||||
|
const result = Utils.recordToMap(record);
|
||||||
|
expect(result).toEqual(
|
||||||
|
new Map([
|
||||||
|
[1, "value1"],
|
||||||
|
[2, "value2"],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
expect(Utils.mapToRecord(result)).toEqual(record);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -423,6 +423,40 @@ export class Utils {
|
|||||||
return this.global.bitwardenContainerService;
|
return this.global.bitwardenContainerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts map to a Record<string, V> with the same data. Inverse of recordToMap
|
||||||
|
* Useful in toJSON methods, since Maps are not serializable
|
||||||
|
* @param map
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static mapToRecord<K extends string | number | symbol, V>(map: Map<K, V>): Record<string, V> {
|
||||||
|
return map == null ? null : Object.fromEntries(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts record to a Map<string, V> with the same data. Inverse of mapToRecord
|
||||||
|
* Useful in fromJSON methods, since Maps are not serializable
|
||||||
|
*
|
||||||
|
* Warning: If the record has string keys that are numbers, they will be converted to numbers in the map
|
||||||
|
* @param record
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static recordToMap<K extends string | number, V>(record: Record<K, V>): Map<K, V> {
|
||||||
|
if (record == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const entries = Object.entries(record);
|
||||||
|
if (entries.length === 0) {
|
||||||
|
return new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(Number(entries[0][0]))) {
|
||||||
|
return new Map(entries) as Map<K, V>;
|
||||||
|
} else {
|
||||||
|
return new Map(entries.map((e) => [Number(e[0]), e[1]])) as Map<K, V>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static isMobile(win: Window) {
|
private static isMobile(win: Window) {
|
||||||
let mobile = false;
|
let mobile = false;
|
||||||
((a) => {
|
((a) => {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { StorageLocation } from "../enums/storageLocation";
|
|||||||
import { ThemeType } from "../enums/themeType";
|
import { ThemeType } from "../enums/themeType";
|
||||||
import { UriMatchType } from "../enums/uriMatchType";
|
import { UriMatchType } from "../enums/uriMatchType";
|
||||||
import { StateFactory } from "../factories/stateFactory";
|
import { StateFactory } from "../factories/stateFactory";
|
||||||
|
import { Utils } from "../misc/utils";
|
||||||
import { CipherData } from "../models/data/cipher.data";
|
import { CipherData } from "../models/data/cipher.data";
|
||||||
import { CollectionData } from "../models/data/collection.data";
|
import { CollectionData } from "../models/data/collection.data";
|
||||||
import { EncryptedOrganizationKeyData } from "../models/data/encrypted-organization-key.data";
|
import { EncryptedOrganizationKeyData } from "../models/data/encrypted-organization-key.data";
|
||||||
@@ -65,13 +66,13 @@ export class StateService<
|
|||||||
TAccount extends Account = Account
|
TAccount extends Account = Account
|
||||||
> implements StateServiceAbstraction<TAccount>
|
> implements StateServiceAbstraction<TAccount>
|
||||||
{
|
{
|
||||||
private accountsSubject = new BehaviorSubject<{ [userId: string]: TAccount }>({});
|
protected accountsSubject = new BehaviorSubject<{ [userId: string]: TAccount }>({});
|
||||||
accounts$ = this.accountsSubject.asObservable();
|
accounts$ = this.accountsSubject.asObservable();
|
||||||
|
|
||||||
private activeAccountSubject = new BehaviorSubject<string | null>(null);
|
protected activeAccountSubject = new BehaviorSubject<string | null>(null);
|
||||||
activeAccount$ = this.activeAccountSubject.asObservable();
|
activeAccount$ = this.activeAccountSubject.asObservable();
|
||||||
|
|
||||||
private activeAccountUnlockedSubject = new BehaviorSubject<boolean>(false);
|
protected activeAccountUnlockedSubject = new BehaviorSubject<boolean>(false);
|
||||||
activeAccountUnlocked$ = this.activeAccountUnlockedSubject.asObservable();
|
activeAccountUnlocked$ = this.activeAccountUnlockedSubject.asObservable();
|
||||||
|
|
||||||
private hasBeenInited = false;
|
private hasBeenInited = false;
|
||||||
@@ -676,7 +677,7 @@ export class StateService<
|
|||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
);
|
);
|
||||||
return this.recordToMap(account?.keys?.organizationKeys?.decrypted);
|
return Utils.recordToMap(account?.keys?.organizationKeys?.decrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDecryptedOrganizationKeys(
|
async setDecryptedOrganizationKeys(
|
||||||
@@ -686,7 +687,7 @@ export class StateService<
|
|||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
);
|
);
|
||||||
account.keys.organizationKeys.decrypted = this.mapToRecord(value);
|
account.keys.organizationKeys.decrypted = Utils.mapToRecord(value);
|
||||||
await this.saveAccount(
|
await this.saveAccount(
|
||||||
account,
|
account,
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
@@ -774,7 +775,7 @@ export class StateService<
|
|||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
);
|
);
|
||||||
return this.recordToMap(account?.keys?.providerKeys?.decrypted);
|
return Utils.recordToMap(account?.keys?.providerKeys?.decrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDecryptedProviderKeys(
|
async setDecryptedProviderKeys(
|
||||||
@@ -784,7 +785,7 @@ export class StateService<
|
|||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
);
|
);
|
||||||
account.keys.providerKeys.decrypted = this.mapToRecord(value);
|
account.keys.providerKeys.decrypted = Utils.mapToRecord(value);
|
||||||
await this.saveAccount(
|
await this.saveAccount(
|
||||||
account,
|
account,
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
@@ -2765,14 +2766,6 @@ export class StateService<
|
|||||||
await this.setState(updatedState);
|
await this.setState(updatedState);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapToRecord<V>(map: Map<string, V>): Record<string, V> {
|
|
||||||
return map == null ? null : Object.fromEntries(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
private recordToMap<V>(record: Record<string, V>): Map<string, V> {
|
|
||||||
return record == null ? null : new Map(Object.entries(record));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withPrototype<T>(
|
export function withPrototype<T>(
|
||||||
|
|||||||
Reference in New Issue
Block a user