From 4f9b2b618f031221536c14e28528e4d8ff44afc1 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Thu, 31 Jul 2025 09:09:14 -0400 Subject: [PATCH] [PM-24280] Remove account service from state (#15828) * Introduce ActiveUserAccessor * Use ActiveUserAccessor over AccountService * Updates tests and testing utils to support ActiveUserAccessor * Update all injection points * Fix types test * Use ternary instead --- .../browser/src/background/main.background.ts | 3 +- .../service-container/service-container.ts | 3 +- apps/desktop/src/main.ts | 3 +- .../src/services/jslib-services.module.ts | 9 ++- libs/common/spec/fake-state-provider.ts | 55 +++++++++++++++---- libs/common/spec/fake-state.ts | 16 ++---- .../services/default-active-user.accessor.ts | 19 +++++++ .../platform/state/active-user.accessor.ts | 11 ++++ ...default-active-user-state.provider.spec.ts | 37 ------------- .../default-active-user-state.provider.ts | 9 ++- .../default-active-user-state.spec.ts | 18 ++---- .../default-state.provider.spec.ts | 34 ++++-------- .../specific-state.provider.spec.ts | 4 +- libs/common/src/platform/state/index.ts | 1 + 14 files changed, 118 insertions(+), 104 deletions(-) create mode 100644 libs/common/src/auth/services/default-active-user.accessor.ts create mode 100644 libs/common/src/platform/state/active-user.accessor.ts delete mode 100644 libs/common/src/platform/state/implementations/default-active-user-state.provider.spec.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 00de187346..7e2d31ba43 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -43,6 +43,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; +import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/default-active-user.accessor"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service"; @@ -591,7 +592,7 @@ export default class MainBackground { this.singleUserStateProvider, ); this.activeUserStateProvider = new DefaultActiveUserStateProvider( - this.accountService, + new DefaultActiveUserAccessor(this.accountService), this.singleUserStateProvider, ); this.derivedStateProvider = new InlineDerivedStateProvider(); diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 3ea471dac5..a5237453f1 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -44,6 +44,7 @@ import { } from "@bitwarden/common/auth/services/account.service"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; +import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/default-active-user.accessor"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation"; import { TokenService } from "@bitwarden/common/auth/services/token.service"; @@ -377,7 +378,7 @@ export class ServiceContainer { ); this.activeUserStateProvider = new DefaultActiveUserStateProvider( - this.accountService, + new DefaultActiveUserAccessor(this.accountService), this.singleUserStateProvider, ); diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 9b5aa0b31e..67fbf457a7 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -9,6 +9,7 @@ import { Subject, firstValueFrom } from "rxjs"; import { SsoUrlService } from "@bitwarden/auth/common"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; +import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/default-active-user.accessor"; import { ClientType } from "@bitwarden/common/enums"; import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; import { RegionConfig } from "@bitwarden/common/platform/abstractions/environment.service"; @@ -170,7 +171,7 @@ export class Main { ); const activeUserStateProvider = new DefaultActiveUserStateProvider( - accountService, + new DefaultActiveUserAccessor(accountService), singleUserStateProvider, ); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index cdc16ba129..113a8ab526 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -110,6 +110,7 @@ import { AccountServiceImplementation } from "@bitwarden/common/auth/services/ac import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.service"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; +import { DefaultActiveUserAccessor } from "@bitwarden/common/auth/services/default-active-user.accessor"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation"; @@ -232,6 +233,7 @@ import { StorageServiceProvider } from "@bitwarden/common/platform/services/stor import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; import { ValidationService } from "@bitwarden/common/platform/services/validation.service"; import { + ActiveUserAccessor, ActiveUserStateProvider, DerivedStateProvider, GlobalStateProvider, @@ -1271,10 +1273,15 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultGlobalStateProvider, deps: [StorageServiceProvider, LogService], }), + safeProvider({ + provide: ActiveUserAccessor, + useClass: DefaultActiveUserAccessor, + deps: [AccountServiceAbstraction], + }), safeProvider({ provide: ActiveUserStateProvider, useClass: DefaultActiveUserStateProvider, - deps: [AccountServiceAbstraction, SingleUserStateProvider], + deps: [ActiveUserAccessor, SingleUserStateProvider], }), safeProvider({ provide: SingleUserStateProvider, diff --git a/libs/common/spec/fake-state-provider.ts b/libs/common/spec/fake-state-provider.ts index 9f72ccada5..d666040205 100644 --- a/libs/common/spec/fake-state-provider.ts +++ b/libs/common/spec/fake-state-provider.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { mock } from "jest-mock-extended"; -import { Observable, map, of, switchMap, take } from "rxjs"; +import { BehaviorSubject, map, Observable, of, switchMap, take } from "rxjs"; import { GlobalState, @@ -16,11 +16,11 @@ import { DeriveDefinition, DerivedStateProvider, UserKeyDefinition, + ActiveUserAccessor, } from "../src/platform/state"; import { UserId } from "../src/types/guid"; import { DerivedStateDependencies } from "../src/types/state"; -import { FakeAccountService } from "./fake-account-service"; import { FakeActiveUserState, FakeDerivedState, @@ -28,6 +28,35 @@ import { FakeSingleUserState, } from "./fake-state"; +export interface MinimalAccountService { + activeUserId: UserId | null; + activeAccount$: Observable<{ id: UserId } | null>; +} + +export class FakeActiveUserAccessor implements MinimalAccountService, ActiveUserAccessor { + private _subject: BehaviorSubject; + + constructor(startingUser: UserId | null) { + this._subject = new BehaviorSubject(startingUser); + this.activeAccount$ = this._subject + .asObservable() + .pipe(map((id) => (id != null ? { id } : null))); + this.activeUserId$ = this._subject.asObservable(); + } + + get activeUserId(): UserId { + return this._subject.value; + } + + activeUserId$: Observable; + + activeAccount$: Observable<{ id: UserId }>; + + switch(user: UserId | null) { + this._subject.next(user); + } +} + export class FakeGlobalStateProvider implements GlobalStateProvider { mock = mock(); establishedMocks: Map> = new Map(); @@ -138,18 +167,18 @@ export class FakeSingleUserStateProvider implements SingleUserStateProvider { } export class FakeActiveUserStateProvider implements ActiveUserStateProvider { - activeUserId$: Observable; + activeUserId$: Observable; states: Map> = new Map(); constructor( - public accountService: FakeAccountService, + public accountServiceAccessor: MinimalAccountService, readonly updateSyncCallback?: ( key: UserKeyDefinition, userId: UserId, newValue: unknown, ) => Promise, ) { - this.activeUserId$ = accountService.activeAccountSubject.asObservable().pipe(map((a) => a?.id)); + this.activeUserId$ = accountServiceAccessor.activeAccount$.pipe(map((a) => a?.id)); } get(userKeyDefinition: UserKeyDefinition): ActiveUserState { @@ -182,9 +211,13 @@ export class FakeActiveUserStateProvider implements ActiveUserStateProvider { } private buildFakeState(userKeyDefinition: UserKeyDefinition, initialValue?: T) { - const state = new FakeActiveUserState(this.accountService, initialValue, async (...args) => { - await this.updateSyncCallback?.(userKeyDefinition, ...args); - }); + const state = new FakeActiveUserState( + this.accountServiceAccessor, + initialValue, + async (...args) => { + await this.updateSyncCallback?.(userKeyDefinition, ...args); + }, + ); state.keyDefinition = userKeyDefinition; return state; } @@ -256,14 +289,14 @@ export class FakeStateProvider implements StateProvider { return this.derived.get(parentState$, deriveDefinition, dependencies); } - constructor(public accountService: FakeAccountService) {} + constructor(private activeAccountAccessor: MinimalAccountService) {} private distributeSingleUserUpdate( key: UserKeyDefinition, userId: UserId, newState: unknown, ) { - if (this.activeUser.accountService.activeUserId === userId) { + if (this.activeUser.accountServiceAccessor.activeUserId === userId) { const state = this.activeUser.getFake(key, { allowInit: false }); state?.nextState(newState, { syncValue: false }); } @@ -284,7 +317,7 @@ export class FakeStateProvider implements StateProvider { this.distributeSingleUserUpdate.bind(this), ); activeUser: FakeActiveUserStateProvider = new FakeActiveUserStateProvider( - this.accountService, + this.activeAccountAccessor, this.distributeActiveUserUpdate.bind(this), ); derived: FakeDerivedStateProvider = new FakeDerivedStateProvider(); diff --git a/libs/common/spec/fake-state.ts b/libs/common/spec/fake-state.ts index d4fbd10847..38019a3868 100644 --- a/libs/common/spec/fake-state.ts +++ b/libs/common/spec/fake-state.ts @@ -18,7 +18,7 @@ import { CombinedState, activeMarker } from "../src/platform/state/user-state"; import { UserId } from "../src/types/guid"; import { DerivedStateDependencies } from "../src/types/state"; -import { FakeAccountService } from "./fake-account-service"; +import { MinimalAccountService } from "./fake-state-provider"; const DEFAULT_TEST_OPTIONS: StateUpdateOptions = { shouldUpdate: () => true, @@ -177,7 +177,7 @@ export class FakeActiveUserState implements ActiveUserState { combinedState$: Observable>; constructor( - private accountService: FakeAccountService, + private activeAccountAccessor: MinimalAccountService, initialValue?: T, updateSyncCallback?: (userId: UserId, newValue: T) => Promise, ) { @@ -194,14 +194,10 @@ export class FakeActiveUserState implements ActiveUserState { this.state$ = this.combinedState$.pipe(map(([_userId, state]) => state)); } - get userId() { - return this.accountService.activeUserId; - } - nextState(state: T | null, { syncValue }: { syncValue: boolean } = { syncValue: true }) { this.stateSubject.next({ syncValue, - combinedState: [this.userId, state], + combinedState: [this.activeAccountAccessor.activeUserId, state], }); } @@ -216,12 +212,12 @@ export class FakeActiveUserState implements ActiveUserState { ? await firstValueFrom(options.combineLatestWith.pipe(timeout(options.msTimeout))) : null; if (!options.shouldUpdate(current, combinedDependencies)) { - return [this.userId, current]; + return [this.activeAccountAccessor.activeUserId, current]; } const newState = configureState(current, combinedDependencies); this.nextState(newState); - this.nextMock([this.userId, newState]); - return [this.userId, newState]; + this.nextMock([this.activeAccountAccessor.activeUserId, newState]); + return [this.activeAccountAccessor.activeUserId, newState]; } /** Tracks update values resolved by `FakeState.update` */ diff --git a/libs/common/src/auth/services/default-active-user.accessor.ts b/libs/common/src/auth/services/default-active-user.accessor.ts new file mode 100644 index 0000000000..05775df945 --- /dev/null +++ b/libs/common/src/auth/services/default-active-user.accessor.ts @@ -0,0 +1,19 @@ +import { map, Observable } from "rxjs"; + +import { UserId } from "@bitwarden/user-core"; + +import { ActiveUserAccessor } from "../../platform/state"; +import { AccountService } from "../abstractions/account.service"; + +/** + * Implementation for Platform so they can avoid a direct dependency on AccountService. Not for general consumption. + */ +export class DefaultActiveUserAccessor implements ActiveUserAccessor { + constructor(private readonly accountService: AccountService) { + this.activeUserId$ = this.accountService.activeAccount$.pipe( + map((a) => (a != null ? a.id : null)), + ); + } + + activeUserId$: Observable; +} diff --git a/libs/common/src/platform/state/active-user.accessor.ts b/libs/common/src/platform/state/active-user.accessor.ts new file mode 100644 index 0000000000..8ee2d53a93 --- /dev/null +++ b/libs/common/src/platform/state/active-user.accessor.ts @@ -0,0 +1,11 @@ +import { Observable } from "rxjs"; + +import { UserId } from "@bitwarden/user-core"; + +export abstract class ActiveUserAccessor { + /** + * Returns a stream of the current active user for the application. The stream either emits the user id for that account + * or returns null if there is no current active user. + */ + abstract activeUserId$: Observable; +} diff --git a/libs/common/src/platform/state/implementations/default-active-user-state.provider.spec.ts b/libs/common/src/platform/state/implementations/default-active-user-state.provider.spec.ts deleted file mode 100644 index 681963f823..0000000000 --- a/libs/common/src/platform/state/implementations/default-active-user-state.provider.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { mock } from "jest-mock-extended"; - -import { mockAccountServiceWith, trackEmissions } from "../../../../spec"; -import { UserId } from "../../../types/guid"; -import { SingleUserStateProvider } from "../user-state.provider"; - -import { DefaultActiveUserStateProvider } from "./default-active-user-state.provider"; - -describe("DefaultActiveUserStateProvider", () => { - const singleUserStateProvider = mock(); - const userId = "userId" as UserId; - const accountInfo = { - id: userId, - name: "name", - email: "email", - emailVerified: false, - }; - const accountService = mockAccountServiceWith(userId, accountInfo); - let sut: DefaultActiveUserStateProvider; - - beforeEach(() => { - sut = new DefaultActiveUserStateProvider(accountService, singleUserStateProvider); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it("should track the active User id from account service", () => { - const emissions = trackEmissions(sut.activeUserId$); - - accountService.activeAccountSubject.next(undefined); - accountService.activeAccountSubject.next(accountInfo); - - expect(emissions).toEqual([userId, undefined, userId]); - }); -}); diff --git a/libs/common/src/platform/state/implementations/default-active-user-state.provider.ts b/libs/common/src/platform/state/implementations/default-active-user-state.provider.ts index 91e6f37c41..d24d2f8df7 100644 --- a/libs/common/src/platform/state/implementations/default-active-user-state.provider.ts +++ b/libs/common/src/platform/state/implementations/default-active-user-state.provider.ts @@ -1,9 +1,9 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Observable, distinctUntilChanged, map } from "rxjs"; +import { Observable, distinctUntilChanged } from "rxjs"; -import { AccountService } from "../../../auth/abstractions/account.service"; import { UserId } from "../../../types/guid"; +import { ActiveUserAccessor } from "../active-user.accessor"; import { UserKeyDefinition } from "../user-key-definition"; import { ActiveUserState } from "../user-state"; import { ActiveUserStateProvider, SingleUserStateProvider } from "../user-state.provider"; @@ -14,11 +14,10 @@ export class DefaultActiveUserStateProvider implements ActiveUserStateProvider { activeUserId$: Observable; constructor( - private readonly accountService: AccountService, + private readonly activeAccountAccessor: ActiveUserAccessor, private readonly singleUserStateProvider: SingleUserStateProvider, ) { - this.activeUserId$ = this.accountService.activeAccount$.pipe( - map((account) => account?.id), + this.activeUserId$ = this.activeAccountAccessor.activeUserId$.pipe( // To avoid going to storage when we don't need to, only get updates when there is a true change. distinctUntilChanged((a, b) => (a == null || b == null ? a == b : a === b)), // Treat null and undefined as equal ); diff --git a/libs/common/src/platform/state/implementations/default-active-user-state.spec.ts b/libs/common/src/platform/state/implementations/default-active-user-state.spec.ts index 1cb1453a50..f6673e66c1 100644 --- a/libs/common/src/platform/state/implementations/default-active-user-state.spec.ts +++ b/libs/common/src/platform/state/implementations/default-active-user-state.spec.ts @@ -3,14 +3,13 @@ * @jest-environment ../shared/test.environment.ts */ import { any, mock } from "jest-mock-extended"; -import { BehaviorSubject, firstValueFrom, map, of, timeout } from "rxjs"; +import { BehaviorSubject, firstValueFrom, of, timeout } from "rxjs"; import { Jsonify } from "type-fest"; import { StorageServiceProvider } from "@bitwarden/storage-core"; import { awaitAsync, trackEmissions } from "../../../../spec"; import { FakeStorageService } from "../../../../spec/fake-storage.service"; -import { Account } from "../../../auth/abstractions/account.service"; import { UserId } from "../../../types/guid"; import { LogService } from "../../abstractions/log.service"; import { StateDefinition } from "../state-definition"; @@ -48,7 +47,7 @@ describe("DefaultActiveUserState", () => { const storageServiceProvider = mock(); const stateEventRegistrarService = mock(); const logService = mock(); - let activeAccountSubject: BehaviorSubject; + let activeAccountSubject: BehaviorSubject; let singleUserStateProvider: DefaultSingleUserStateProvider; @@ -64,11 +63,11 @@ describe("DefaultActiveUserState", () => { logService, ); - activeAccountSubject = new BehaviorSubject(null); + activeAccountSubject = new BehaviorSubject(null); userState = new DefaultActiveUserState( testKeyDefinition, - activeAccountSubject.asObservable().pipe(map((a) => a?.id)), + activeAccountSubject.asObservable(), singleUserStateProvider, ); }); @@ -83,12 +82,7 @@ describe("DefaultActiveUserState", () => { const changeActiveUser = async (id: string) => { const userId = makeUserId(id); - activeAccountSubject.next({ - id: userId, - email: `test${id}@example.com`, - emailVerified: false, - name: `Test User ${id}`, - }); + activeAccountSubject.next(userId); await awaitAsync(); }; @@ -588,7 +582,7 @@ describe("DefaultActiveUserState", () => { }); it("does not await updates if the active user changes", async () => { - const initialUserId = (await firstValueFrom(activeAccountSubject)).id; + const initialUserId = activeAccountSubject.value; expect(initialUserId).toBe(userId); trackEmissions(userState.state$); await awaitAsync(); // storage updates are behind a promise diff --git a/libs/common/src/platform/state/implementations/default-state.provider.spec.ts b/libs/common/src/platform/state/implementations/default-state.provider.spec.ts index b3190bd532..442cec66d9 100644 --- a/libs/common/src/platform/state/implementations/default-state.provider.spec.ts +++ b/libs/common/src/platform/state/implementations/default-state.provider.spec.ts @@ -4,16 +4,16 @@ */ import { Observable, of } from "rxjs"; +import { UserId } from "@bitwarden/user-core"; + import { awaitAsync, trackEmissions } from "../../../../spec"; -import { FakeAccountService, mockAccountServiceWith } from "../../../../spec/fake-account-service"; import { + FakeActiveUserAccessor, FakeActiveUserStateProvider, FakeDerivedStateProvider, FakeGlobalStateProvider, FakeSingleUserStateProvider, } from "../../../../spec/fake-state-provider"; -import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; -import { UserId } from "../../../types/guid"; import { DeriveDefinition } from "../derive-definition"; import { KeyDefinition } from "../key-definition"; import { StateDefinition } from "../state-definition"; @@ -27,12 +27,12 @@ describe("DefaultStateProvider", () => { let singleUserStateProvider: FakeSingleUserStateProvider; let globalStateProvider: FakeGlobalStateProvider; let derivedStateProvider: FakeDerivedStateProvider; - let accountService: FakeAccountService; + let activeAccountAccessor: FakeActiveUserAccessor; const userId = "fakeUserId" as UserId; beforeEach(() => { - accountService = mockAccountServiceWith(userId); - activeUserStateProvider = new FakeActiveUserStateProvider(accountService); + activeAccountAccessor = new FakeActiveUserAccessor(userId); + activeUserStateProvider = new FakeActiveUserStateProvider(activeAccountAccessor); singleUserStateProvider = new FakeSingleUserStateProvider(); globalStateProvider = new FakeGlobalStateProvider(); derivedStateProvider = new FakeDerivedStateProvider(); @@ -70,12 +70,6 @@ describe("DefaultStateProvider", () => { userId?: UserId, ) => Observable, ) => { - const accountInfo = { - email: "email", - emailVerified: false, - name: "name", - status: AuthenticationStatus.LoggedOut, - }; const keyDefinition = new UserKeyDefinition( new StateDefinition("test", "disk"), "test", @@ -97,7 +91,7 @@ describe("DefaultStateProvider", () => { }); it("should follow the current active user if no userId is provided", async () => { - accountService.activeAccountSubject.next({ id: userId, ...accountInfo }); + activeAccountAccessor.switch(userId); const state = singleUserStateProvider.getFake(userId, keyDefinition); state.nextState("value"); const emissions = trackEmissions(methodUnderTest(keyDefinition)); @@ -113,7 +107,7 @@ describe("DefaultStateProvider", () => { state.nextState("value"); const emissions = trackEmissions(methodUnderTest(keyDefinition)); - accountService.activeAccountSubject.next({ id: "newUserId" as UserId, ...accountInfo }); + activeAccountAccessor.switch("newUserId" as UserId); const newUserEmissions = trackEmissions(sut.getUserState$(keyDefinition)); state.nextState("value2"); state.nextState("value3"); @@ -125,12 +119,6 @@ describe("DefaultStateProvider", () => { ); describe("getUserState$", () => { - const accountInfo = { - email: "email", - emailVerified: false, - name: "name", - status: AuthenticationStatus.LoggedOut, - }; const keyDefinition = new UserKeyDefinition( new StateDefinition("test", "disk"), "test", @@ -141,7 +129,7 @@ describe("DefaultStateProvider", () => { ); it("should not emit any values until a truthy user id is supplied", async () => { - accountService.activeAccountSubject.next(null); + activeAccountAccessor.switch(null); const state = singleUserStateProvider.getFake(userId, keyDefinition); state.nextState("value"); @@ -151,7 +139,7 @@ describe("DefaultStateProvider", () => { expect(emissions).toHaveLength(0); - accountService.activeAccountSubject.next({ id: userId, ...accountInfo }); + activeAccountAccessor.switch(userId); await awaitAsync(); @@ -170,7 +158,7 @@ describe("DefaultStateProvider", () => { ); it("should emit default value if no userId supplied and first active user id emission in falsy", async () => { - accountService.activeAccountSubject.next(null); + activeAccountAccessor.switch(null); const emissions = trackEmissions( sut.getUserStateOrDefault$(keyDefinition, { diff --git a/libs/common/src/platform/state/implementations/specific-state.provider.spec.ts b/libs/common/src/platform/state/implementations/specific-state.provider.spec.ts index 6674c2577d..701274eca3 100644 --- a/libs/common/src/platform/state/implementations/specific-state.provider.spec.ts +++ b/libs/common/src/platform/state/implementations/specific-state.provider.spec.ts @@ -2,7 +2,7 @@ import { mock } from "jest-mock-extended"; import { StorageServiceProvider } from "@bitwarden/storage-core"; -import { mockAccountServiceWith } from "../../../../spec/fake-account-service"; +import { FakeActiveUserAccessor } from "../../../../spec"; import { FakeStorageService } from "../../../../spec/fake-storage.service"; import { UserId } from "../../../types/guid"; import { LogService } from "../../abstractions/log.service"; @@ -39,7 +39,7 @@ describe("Specific State Providers", () => { stateEventRegistrarService, logService, ); - activeSut = new DefaultActiveUserStateProvider(mockAccountServiceWith(null), singleSut); + activeSut = new DefaultActiveUserStateProvider(new FakeActiveUserAccessor(null), singleSut); globalSut = new DefaultGlobalStateProvider(storageServiceProvider, logService); }); diff --git a/libs/common/src/platform/state/index.ts b/libs/common/src/platform/state/index.ts index 367beefb49..6dc1409050 100644 --- a/libs/common/src/platform/state/index.ts +++ b/libs/common/src/platform/state/index.ts @@ -10,5 +10,6 @@ export { KeyDefinition, KeyDefinitionOptions } from "./key-definition"; export { StateUpdateOptions } from "./state-update-options"; export { UserKeyDefinitionOptions, UserKeyDefinition } from "./user-key-definition"; export { StateEventRunnerService } from "./state-event-runner.service"; +export { ActiveUserAccessor } from "./active-user.accessor"; export * from "./state-definitions";