mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
Build Account from a StateService provided AccountDeserializer
This commit is contained in:
@@ -4,22 +4,25 @@ import { State } from "./state";
|
|||||||
describe("state", () => {
|
describe("state", () => {
|
||||||
describe("fromJSON", () => {
|
describe("fromJSON", () => {
|
||||||
it("should deserialize to an instance of itself", () => {
|
it("should deserialize to an instance of itself", () => {
|
||||||
expect(State.fromJSON({})).toBeInstanceOf(State);
|
expect(State.fromJSON({}, () => new Account({}))).toBeInstanceOf(State);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should always assign an object to accounts", () => {
|
it("should always assign an object to accounts", () => {
|
||||||
const state = State.fromJSON({});
|
const state = State.fromJSON({}, () => new Account({}));
|
||||||
expect(state.accounts).not.toBeNull();
|
expect(state.accounts).not.toBeNull();
|
||||||
expect(state.accounts).toEqual({});
|
expect(state.accounts).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should build an account map", () => {
|
it("should build an account map", () => {
|
||||||
const accountsSpy = jest.spyOn(Account, "fromJSON");
|
const accountsSpy = jest.spyOn(Account, "fromJSON");
|
||||||
const state = State.fromJSON({
|
const state = State.fromJSON(
|
||||||
accounts: {
|
{
|
||||||
userId: {},
|
accounts: {
|
||||||
|
userId: {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
Account.fromJSON
|
||||||
|
);
|
||||||
|
|
||||||
expect(state.accounts["userId"]).toBeInstanceOf(Account);
|
expect(state.accounts["userId"]).toBeInstanceOf(Account);
|
||||||
expect(accountsSpy).toHaveBeenCalled();
|
expect(accountsSpy).toHaveBeenCalled();
|
||||||
|
|||||||
@@ -19,26 +19,28 @@ export class State<
|
|||||||
|
|
||||||
// TODO, make Jsonify<State,TGlobalState,TAccount> work. It currently doesn't because Globals doesn't implement Jsonify.
|
// TODO, make Jsonify<State,TGlobalState,TAccount> work. It currently doesn't because Globals doesn't implement Jsonify.
|
||||||
static fromJSON<TGlobalState extends GlobalState, TAccount extends Account>(
|
static fromJSON<TGlobalState extends GlobalState, TAccount extends Account>(
|
||||||
obj: any
|
obj: any,
|
||||||
|
accountDeserializer: (json: Jsonify<TAccount>) => TAccount
|
||||||
): State<TGlobalState, TAccount> {
|
): State<TGlobalState, TAccount> {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(new State(null), obj, {
|
return Object.assign(new State(null), obj, {
|
||||||
accounts: State.buildAccountMapFromJSON(obj?.accounts),
|
accounts: State.buildAccountMapFromJSON(obj?.accounts, accountDeserializer),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static buildAccountMapFromJSON(
|
private static buildAccountMapFromJSON<TAccount extends Account>(
|
||||||
jsonAccounts: Jsonify<{ [userId: string]: Jsonify<Account> }>
|
jsonAccounts: { [userId: string]: Jsonify<TAccount> },
|
||||||
|
accountDeserializer: (json: Jsonify<TAccount>) => TAccount
|
||||||
) {
|
) {
|
||||||
if (!jsonAccounts) {
|
if (!jsonAccounts) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const accounts: { [userId: string]: Account } = {};
|
const accounts: { [userId: string]: TAccount } = {};
|
||||||
for (const userId in jsonAccounts) {
|
for (const userId in jsonAccounts) {
|
||||||
accounts[userId] = Account.fromJSON(jsonAccounts[userId]);
|
accounts[userId] = accountDeserializer(jsonAccounts[userId]);
|
||||||
}
|
}
|
||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BehaviorSubject, concatMap } from "rxjs";
|
import { BehaviorSubject, concatMap } from "rxjs";
|
||||||
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { LogService } from "../abstractions/log.service";
|
import { LogService } from "../abstractions/log.service";
|
||||||
import { StateService as StateServiceAbstraction } from "../abstractions/state.service";
|
import { StateService as StateServiceAbstraction } from "../abstractions/state.service";
|
||||||
@@ -80,6 +81,9 @@ export class StateService<
|
|||||||
|
|
||||||
private accountDiskCache = new Map<string, TAccount>();
|
private accountDiskCache = new Map<string, TAccount>();
|
||||||
|
|
||||||
|
// default account serializer, must be overridden by child class
|
||||||
|
protected accountDeserializer = Account.fromJSON as (json: Jsonify<TAccount>) => TAccount;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected storageService: AbstractStorageService,
|
protected storageService: AbstractStorageService,
|
||||||
protected secureStorageService: AbstractStorageService,
|
protected secureStorageService: AbstractStorageService,
|
||||||
@@ -2745,7 +2749,7 @@ export class StateService<
|
|||||||
|
|
||||||
protected async state(): Promise<State<TGlobalState, TAccount>> {
|
protected async state(): Promise<State<TGlobalState, TAccount>> {
|
||||||
const state = await this.memoryStorageService.get<State<TGlobalState, TAccount>>(keys.state, {
|
const state = await this.memoryStorageService.get<State<TGlobalState, TAccount>>(keys.state, {
|
||||||
deserializer: (s) => State.fromJSON(s),
|
deserializer: (s) => State.fromJSON(s, this.accountDeserializer),
|
||||||
});
|
});
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user