1
0
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:
Matt Gibson
2022-11-16 18:52:38 -05:00
parent 20e748a4b9
commit 04f5e6a144
3 changed files with 22 additions and 13 deletions

View File

@@ -4,22 +4,25 @@ import { State } from "./state";
describe("state", () => {
describe("fromJSON", () => {
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", () => {
const state = State.fromJSON({});
const state = State.fromJSON({}, () => new Account({}));
expect(state.accounts).not.toBeNull();
expect(state.accounts).toEqual({});
});
it("should build an account map", () => {
const accountsSpy = jest.spyOn(Account, "fromJSON");
const state = State.fromJSON({
accounts: {
userId: {},
const state = State.fromJSON(
{
accounts: {
userId: {},
},
},
});
Account.fromJSON
);
expect(state.accounts["userId"]).toBeInstanceOf(Account);
expect(accountsSpy).toHaveBeenCalled();

View File

@@ -19,26 +19,28 @@ export class State<
// 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>(
obj: any
obj: any,
accountDeserializer: (json: Jsonify<TAccount>) => TAccount
): State<TGlobalState, TAccount> {
if (obj == null) {
return null;
}
return Object.assign(new State(null), obj, {
accounts: State.buildAccountMapFromJSON(obj?.accounts),
accounts: State.buildAccountMapFromJSON(obj?.accounts, accountDeserializer),
});
}
private static buildAccountMapFromJSON(
jsonAccounts: Jsonify<{ [userId: string]: Jsonify<Account> }>
private static buildAccountMapFromJSON<TAccount extends Account>(
jsonAccounts: { [userId: string]: Jsonify<TAccount> },
accountDeserializer: (json: Jsonify<TAccount>) => TAccount
) {
if (!jsonAccounts) {
return {};
}
const accounts: { [userId: string]: Account } = {};
const accounts: { [userId: string]: TAccount } = {};
for (const userId in jsonAccounts) {
accounts[userId] = Account.fromJSON(jsonAccounts[userId]);
accounts[userId] = accountDeserializer(jsonAccounts[userId]);
}
return accounts;
}

View File

@@ -1,4 +1,5 @@
import { BehaviorSubject, concatMap } from "rxjs";
import { Jsonify } from "type-fest";
import { LogService } from "../abstractions/log.service";
import { StateService as StateServiceAbstraction } from "../abstractions/state.service";
@@ -80,6 +81,9 @@ export class StateService<
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(
protected storageService: AbstractStorageService,
protected secureStorageService: AbstractStorageService,
@@ -2745,7 +2749,7 @@ export class StateService<
protected async state(): Promise<State<TGlobalState, TAccount>> {
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;
}