diff --git a/libs/common/spec/fake-state-provider.ts b/libs/common/spec/fake-state-provider.ts index 2078fe3abd..cd868931f2 100644 --- a/libs/common/spec/fake-state-provider.ts +++ b/libs/common/spec/fake-state-provider.ts @@ -15,8 +15,6 @@ import { DerivedStateProvider, UserKeyDefinition, } from "../src/platform/state"; -// eslint-disable-next-line import/no-restricted-paths -- Needed to type check similarly to the real state providers -import { isUserKeyDefinition } from "../src/platform/state/user-key-definition"; import { UserId } from "../src/types/guid"; import { DerivedStateDependencies } from "../src/types/state"; @@ -71,37 +69,28 @@ export class FakeSingleUserStateProvider implements SingleUserStateProvider { mock = mock(); establishedMocks: Map> = new Map(); states: Map> = new Map(); - get( - userId: UserId, - keyDefinition: KeyDefinition | UserKeyDefinition, - ): SingleUserState { - this.mock.get(userId, keyDefinition); - if (keyDefinition instanceof KeyDefinition) { - keyDefinition = UserKeyDefinition.fromBaseKeyDefinition(keyDefinition); - } - const cacheKey = `${keyDefinition.fullName}_${keyDefinition.stateDefinition.defaultStorageLocation}_${userId}`; + get(userId: UserId, userKeyDefinition: UserKeyDefinition): SingleUserState { + this.mock.get(userId, userKeyDefinition); + const cacheKey = `${userKeyDefinition.fullName}_${userKeyDefinition.stateDefinition.defaultStorageLocation}_${userId}`; let result = this.states.get(cacheKey); if (result == null) { let fake: FakeSingleUserState; // Look for established mock - if (this.establishedMocks.has(keyDefinition.key)) { - fake = this.establishedMocks.get(keyDefinition.key) as FakeSingleUserState; + if (this.establishedMocks.has(userKeyDefinition.key)) { + fake = this.establishedMocks.get(userKeyDefinition.key) as FakeSingleUserState; } else { fake = new FakeSingleUserState(userId); } - fake.keyDefinition = keyDefinition; + fake.keyDefinition = userKeyDefinition; result = fake; this.states.set(cacheKey, result); } return result as SingleUserState; } - getFake( - userId: UserId, - keyDefinition: KeyDefinition | UserKeyDefinition, - ): FakeSingleUserState { - return this.get(userId, keyDefinition) as FakeSingleUserState; + getFake(userId: UserId, userKeyDefinition: UserKeyDefinition): FakeSingleUserState { + return this.get(userId, userKeyDefinition) as FakeSingleUserState; } mockFor(userId: UserId, keyDefinitionKey: string, initialValue?: T): FakeSingleUserState { @@ -122,28 +111,25 @@ export class FakeActiveUserStateProvider implements ActiveUserStateProvider { this.activeUserId$ = accountService.activeAccountSubject.asObservable().pipe(map((a) => a?.id)); } - get(keyDefinition: KeyDefinition | UserKeyDefinition): ActiveUserState { - if (keyDefinition instanceof KeyDefinition) { - keyDefinition = UserKeyDefinition.fromBaseKeyDefinition(keyDefinition); - } - const cacheKey = `${keyDefinition.fullName}_${keyDefinition.stateDefinition.defaultStorageLocation}`; + get(userKeyDefinition: UserKeyDefinition): ActiveUserState { + const cacheKey = `${userKeyDefinition.fullName}_${userKeyDefinition.stateDefinition.defaultStorageLocation}`; let result = this.states.get(cacheKey); if (result == null) { // Look for established mock - if (this.establishedMocks.has(keyDefinition.key)) { - result = this.establishedMocks.get(keyDefinition.key); + if (this.establishedMocks.has(userKeyDefinition.key)) { + result = this.establishedMocks.get(userKeyDefinition.key); } else { result = new FakeActiveUserState(this.accountService); } - result.keyDefinition = keyDefinition; + result.keyDefinition = userKeyDefinition; this.states.set(cacheKey, result); } return result as ActiveUserState; } - getFake(keyDefinition: KeyDefinition | UserKeyDefinition): FakeActiveUserState { - return this.get(keyDefinition) as FakeActiveUserState; + getFake(userKeyDefinition: UserKeyDefinition): FakeActiveUserState { + return this.get(userKeyDefinition) as FakeActiveUserState; } mockFor(keyDefinitionKey: string, initialValue?: T): FakeActiveUserState { @@ -159,70 +145,56 @@ export class FakeActiveUserStateProvider implements ActiveUserStateProvider { export class FakeStateProvider implements StateProvider { mock = mock(); - getUserState$( - keyDefinition: KeyDefinition | UserKeyDefinition, - userId?: UserId, - ): Observable { - if (isUserKeyDefinition(keyDefinition)) { - this.mock.getUserState$(keyDefinition, userId); - } else { - this.mock.getUserState$(keyDefinition, userId); - } + getUserState$(userKeyDefinition: UserKeyDefinition, userId?: UserId): Observable { + this.mock.getUserState$(userKeyDefinition, userId); if (userId) { - return this.getUser(userId, keyDefinition).state$; + return this.getUser(userId, userKeyDefinition).state$; } - return this.getActive(keyDefinition).state$; + return this.getActive(userKeyDefinition).state$; } getUserStateOrDefault$( - keyDefinition: KeyDefinition | UserKeyDefinition, + userKeyDefinition: UserKeyDefinition, config: { userId: UserId | undefined; defaultValue?: T }, ): Observable { const { userId, defaultValue = null } = config; - if (isUserKeyDefinition(keyDefinition)) { - this.mock.getUserStateOrDefault$(keyDefinition, config); - } else { - this.mock.getUserStateOrDefault$(keyDefinition, config); - } + this.mock.getUserStateOrDefault$(userKeyDefinition, config); if (userId) { - return this.getUser(userId, keyDefinition).state$; + return this.getUser(userId, userKeyDefinition).state$; } return this.activeUserId$.pipe( take(1), switchMap((userId) => - userId != null ? this.getUser(userId, keyDefinition).state$ : of(defaultValue), + userId != null ? this.getUser(userId, userKeyDefinition).state$ : of(defaultValue), ), ); } async setUserState( - keyDefinition: KeyDefinition | UserKeyDefinition, + userKeyDefinition: UserKeyDefinition, value: T, userId?: UserId, ): Promise<[UserId, T]> { - await this.mock.setUserState(keyDefinition, value, userId); + await this.mock.setUserState(userKeyDefinition, value, userId); if (userId) { - return [userId, await this.getUser(userId, keyDefinition).update(() => value)]; + return [userId, await this.getUser(userId, userKeyDefinition).update(() => value)]; } else { - return await this.getActive(keyDefinition).update(() => value); + return await this.getActive(userKeyDefinition).update(() => value); } } - getActive(keyDefinition: KeyDefinition | UserKeyDefinition): ActiveUserState { - return this.activeUser.get(keyDefinition); + getActive(userKeyDefinition: UserKeyDefinition): ActiveUserState { + return this.activeUser.get(userKeyDefinition); } getGlobal(keyDefinition: KeyDefinition): GlobalState { return this.global.get(keyDefinition); } - getUser( - userId: UserId, - keyDefinition: KeyDefinition | UserKeyDefinition, - ): SingleUserState { - return this.singleUser.get(userId, keyDefinition); + getUser(userId: UserId, userKeyDefinition: UserKeyDefinition): SingleUserState { + return this.singleUser.get(userId, userKeyDefinition); } getDerived( 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 3c12477b79..9c81c69d15 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 @@ -2,8 +2,7 @@ import { Observable, distinctUntilChanged, map } from "rxjs"; import { AccountService } from "../../../auth/abstractions/account.service"; import { UserId } from "../../../types/guid"; -import { KeyDefinition } from "../key-definition"; -import { UserKeyDefinition, isUserKeyDefinition } from "../user-key-definition"; +import { UserKeyDefinition } from "../user-key-definition"; import { ActiveUserState } from "../user-state"; import { ActiveUserStateProvider, SingleUserStateProvider } from "../user-state.provider"; @@ -23,11 +22,7 @@ export class DefaultActiveUserStateProvider implements ActiveUserStateProvider { ); } - get(keyDefinition: KeyDefinition | UserKeyDefinition): ActiveUserState { - if (!isUserKeyDefinition(keyDefinition)) { - keyDefinition = UserKeyDefinition.fromBaseKeyDefinition(keyDefinition); - } - + get(keyDefinition: UserKeyDefinition): ActiveUserState { // All other providers cache the creation of their corresponding `State` objects, this instance // doesn't need to do that since it calls `SingleUserStateProvider` it will go through their caching // layer, because of that, the creation of this instance is quite simple and not worth caching. diff --git a/libs/common/src/platform/state/implementations/default-single-user-state.provider.ts b/libs/common/src/platform/state/implementations/default-single-user-state.provider.ts index 9c01c2a76e..a913bb02bd 100644 --- a/libs/common/src/platform/state/implementations/default-single-user-state.provider.ts +++ b/libs/common/src/platform/state/implementations/default-single-user-state.provider.ts @@ -1,8 +1,7 @@ import { UserId } from "../../../types/guid"; import { StorageServiceProvider } from "../../services/storage-service.provider"; -import { KeyDefinition } from "../key-definition"; import { StateEventRegistrarService } from "../state-event-registrar.service"; -import { UserKeyDefinition, isUserKeyDefinition } from "../user-key-definition"; +import { UserKeyDefinition } from "../user-key-definition"; import { SingleUserState } from "../user-state"; import { SingleUserStateProvider } from "../user-state.provider"; @@ -16,13 +15,7 @@ export class DefaultSingleUserStateProvider implements SingleUserStateProvider { private readonly stateEventRegistrarService: StateEventRegistrarService, ) {} - get( - userId: UserId, - keyDefinition: KeyDefinition | UserKeyDefinition, - ): SingleUserState { - if (!isUserKeyDefinition(keyDefinition)) { - keyDefinition = UserKeyDefinition.fromBaseKeyDefinition(keyDefinition); - } + get(userId: UserId, keyDefinition: UserKeyDefinition): SingleUserState { const [location, storageService] = this.storageServiceProvider.get( keyDefinition.stateDefinition.defaultStorageLocation, keyDefinition.stateDefinition.storageLocationOverrides, 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 98d423cf48..5b8b2d1bfe 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 @@ -17,6 +17,7 @@ import { UserId } from "../../../types/guid"; import { DeriveDefinition } from "../derive-definition"; import { KeyDefinition } from "../key-definition"; import { StateDefinition } from "../state-definition"; +import { UserKeyDefinition } from "../user-key-definition"; import { DefaultStateProvider } from "./default-state.provider"; @@ -52,12 +53,12 @@ describe("DefaultStateProvider", () => { describe.each([ [ "getUserState$", - (keyDefinition: KeyDefinition, userId?: UserId) => + (keyDefinition: UserKeyDefinition, userId?: UserId) => sut.getUserState$(keyDefinition, userId), ], [ "getUserStateOrDefault$", - (keyDefinition: KeyDefinition, userId?: UserId) => + (keyDefinition: UserKeyDefinition, userId?: UserId) => sut.getUserStateOrDefault$(keyDefinition, { userId: userId }), ], ])( @@ -65,7 +66,7 @@ describe("DefaultStateProvider", () => { ( _testName: string, methodUnderTest: ( - keyDefinition: KeyDefinition, + keyDefinition: UserKeyDefinition, userId?: UserId, ) => Observable, ) => { @@ -75,9 +76,14 @@ describe("DefaultStateProvider", () => { name: "name", status: AuthenticationStatus.LoggedOut, }; - const keyDefinition = new KeyDefinition(new StateDefinition("test", "disk"), "test", { - deserializer: (s) => s, - }); + const keyDefinition = new UserKeyDefinition( + new StateDefinition("test", "disk"), + "test", + { + deserializer: (s) => s, + clearOn: [], + }, + ); it("should follow the specified user if userId is provided", async () => { const state = singleUserStateProvider.getFake(userId, keyDefinition); @@ -125,9 +131,14 @@ describe("DefaultStateProvider", () => { name: "name", status: AuthenticationStatus.LoggedOut, }; - const keyDefinition = new KeyDefinition(new StateDefinition("test", "disk"), "test", { - deserializer: (s) => s, - }); + const keyDefinition = new UserKeyDefinition( + new StateDefinition("test", "disk"), + "test", + { + deserializer: (s) => s, + clearOn: [], + }, + ); it("should not emit any values until a truthy user id is supplied", async () => { accountService.activeAccountSubject.next(null); @@ -149,9 +160,14 @@ describe("DefaultStateProvider", () => { }); describe("getUserStateOrDefault$", () => { - const keyDefinition = new KeyDefinition(new StateDefinition("test", "disk"), "test", { - deserializer: (s) => s, - }); + const keyDefinition = new UserKeyDefinition( + new StateDefinition("test", "disk"), + "test", + { + deserializer: (s) => s, + clearOn: [], + }, + ); it("should emit default value if no userId supplied and first active user id emission in falsy", async () => { accountService.activeAccountSubject.next(null); @@ -168,9 +184,14 @@ describe("DefaultStateProvider", () => { }); describe("setUserState", () => { - const keyDefinition = new KeyDefinition(new StateDefinition("test", "disk"), "test", { - deserializer: (s) => s, - }); + const keyDefinition = new UserKeyDefinition( + new StateDefinition("test", "disk"), + "test", + { + deserializer: (s) => s, + clearOn: [], + }, + ); it("should set the state for the active user if no userId is provided", async () => { const value = "value"; @@ -202,8 +223,9 @@ describe("DefaultStateProvider", () => { }); it("should bind the activeUserStateProvider", () => { - const keyDefinition = new KeyDefinition(new StateDefinition("test", "disk"), "test", { + const keyDefinition = new UserKeyDefinition(new StateDefinition("test", "disk"), "test", { deserializer: () => null, + clearOn: [], }); const existing = activeUserStateProvider.get(keyDefinition); const actual = sut.getActive(keyDefinition); @@ -212,8 +234,9 @@ describe("DefaultStateProvider", () => { it("should bind the singleUserStateProvider", () => { const userId = "user" as UserId; - const keyDefinition = new KeyDefinition(new StateDefinition("test", "disk"), "test", { + const keyDefinition = new UserKeyDefinition(new StateDefinition("test", "disk"), "test", { deserializer: () => null, + clearOn: [], }); const existing = singleUserStateProvider.get(userId, keyDefinition); const actual = sut.getUser(userId, keyDefinition); diff --git a/libs/common/src/platform/state/implementations/default-state.provider.ts b/libs/common/src/platform/state/implementations/default-state.provider.ts index 1413d26ce5..22aed80e8a 100644 --- a/libs/common/src/platform/state/implementations/default-state.provider.ts +++ b/libs/common/src/platform/state/implementations/default-state.provider.ts @@ -6,7 +6,6 @@ import { DeriveDefinition } from "../derive-definition"; import { DerivedState } from "../derived-state"; import { DerivedStateProvider } from "../derived-state.provider"; import { GlobalStateProvider } from "../global-state.provider"; -import { KeyDefinition } from "../key-definition"; import { StateProvider } from "../state.provider"; import { UserKeyDefinition } from "../user-key-definition"; import { ActiveUserStateProvider, SingleUserStateProvider } from "../user-state.provider"; @@ -22,47 +21,44 @@ export class DefaultStateProvider implements StateProvider { this.activeUserId$ = this.activeUserStateProvider.activeUserId$; } - getUserState$( - keyDefinition: KeyDefinition | UserKeyDefinition, - userId?: UserId, - ): Observable { + getUserState$(userKeyDefinition: UserKeyDefinition, userId?: UserId): Observable { if (userId) { - return this.getUser(userId, keyDefinition).state$; + return this.getUser(userId, userKeyDefinition).state$; } else { return this.activeUserId$.pipe( filter((userId) => userId != null), // Filter out null-ish user ids since we can't get state for a null user id take(1), - switchMap((userId) => this.getUser(userId, keyDefinition).state$), + switchMap((userId) => this.getUser(userId, userKeyDefinition).state$), ); } } getUserStateOrDefault$( - keyDefinition: KeyDefinition | UserKeyDefinition, + userKeyDefinition: UserKeyDefinition, config: { userId: UserId | undefined; defaultValue?: T }, ): Observable { const { userId, defaultValue = null } = config; if (userId) { - return this.getUser(userId, keyDefinition).state$; + return this.getUser(userId, userKeyDefinition).state$; } else { return this.activeUserId$.pipe( take(1), switchMap((userId) => - userId != null ? this.getUser(userId, keyDefinition).state$ : of(defaultValue), + userId != null ? this.getUser(userId, userKeyDefinition).state$ : of(defaultValue), ), ); } } async setUserState( - keyDefinition: KeyDefinition | UserKeyDefinition, + userKeyDefinition: UserKeyDefinition, value: T, userId?: UserId, ): Promise<[UserId, T]> { if (userId) { - return [userId, await this.getUser(userId, keyDefinition).update(() => value)]; + return [userId, await this.getUser(userId, userKeyDefinition).update(() => value)]; } else { - return await this.getActive(keyDefinition).update(() => value); + return await this.getActive(userKeyDefinition).update(() => value); } } 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 da41908935..94e9c9c3e2 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 @@ -7,6 +7,7 @@ import { StorageServiceProvider } from "../../services/storage-service.provider" import { KeyDefinition } from "../key-definition"; import { StateDefinition } from "../state-definition"; import { StateEventRegistrarService } from "../state-event-registrar.service"; +import { UserKeyDefinition } from "../user-key-definition"; import { DefaultActiveUserState } from "./default-active-user-state"; import { DefaultActiveUserStateProvider } from "./default-active-user-state.provider"; @@ -41,94 +42,132 @@ describe("Specific State Providers", () => { const fakeDiskStateDefinition = new StateDefinition("fake", "disk"); const fakeAlternateDiskStateDefinition = new StateDefinition("fakeAlternate", "disk"); const fakeMemoryStateDefinition = new StateDefinition("fake", "memory"); - - const fakeDiskKeyDefinition = new KeyDefinition(fakeDiskStateDefinition, "fake", { - deserializer: (b) => b, - }); - const fakeAlternateKeyDefinition = new KeyDefinition( - fakeAlternateDiskStateDefinition, - "fake", - { + const makeKeyDefinition = (stateDefinition: StateDefinition, key: string) => + new KeyDefinition(stateDefinition, key, { deserializer: (b) => b, - }, - ); - const fakeMemoryKeyDefinition = new KeyDefinition(fakeMemoryStateDefinition, "fake", { - deserializer: (b) => b, - }); - const fakeDiskKeyDefinitionAlternate = new KeyDefinition( - fakeDiskStateDefinition, - "fakeAlternate", - { + }); + const makeUserKeyDefinition = (stateDefinition: StateDefinition, key: string) => + new UserKeyDefinition(stateDefinition, key, { deserializer: (b) => b, + clearOn: [], + }); + const keyDefinitions = { + disk: { + keyDefinition: makeKeyDefinition(fakeDiskStateDefinition, "fake"), + userKeyDefinition: makeUserKeyDefinition(fakeDiskStateDefinition, "fake"), + altKeyDefinition: makeKeyDefinition(fakeDiskStateDefinition, "fakeAlternate"), + altUserKeyDefinition: makeUserKeyDefinition(fakeDiskStateDefinition, "fakeAlternate"), }, - ); + memory: { + keyDefinition: makeKeyDefinition(fakeMemoryStateDefinition, "fake"), + userKeyDefinition: makeUserKeyDefinition(fakeMemoryStateDefinition, "fake"), + }, + alternateDisk: { + keyDefinition: makeKeyDefinition(fakeAlternateDiskStateDefinition, "fake"), + userKeyDefinition: makeUserKeyDefinition(fakeAlternateDiskStateDefinition, "fake"), + }, + }; - const globalAndSingle = [ - { - getMethod: (keyDefinition: KeyDefinition) => globalSut.get(keyDefinition), - expectedInstance: DefaultGlobalState, - }, - { - // Use a static user id so that it has the same signature as the rest and then write special tests - // handling differing user id - getMethod: (keyDefinition: KeyDefinition) => singleSut.get(fakeUser1, keyDefinition), - expectedInstance: DefaultSingleUserState, - }, - ]; + describe("active provider", () => { + it("returns a DefaultActiveUserState", () => { + const state = activeSut.get(keyDefinitions.disk.userKeyDefinition); - describe.each([ - { - getMethod: (keyDefinition: KeyDefinition) => activeSut.get(keyDefinition), - expectedInstance: DefaultActiveUserState, - }, - ...globalAndSingle, - ])("common behavior %s", ({ getMethod, expectedInstance }) => { - it("returns expected instance", () => { - const state = getMethod(fakeDiskKeyDefinition); - - expect(state).toBeTruthy(); - expect(state).toBeInstanceOf(expectedInstance); + expect(state).toBeInstanceOf(DefaultActiveUserState); }); it("returns different instances when the storage location differs", () => { - const stateDisk = getMethod(fakeDiskKeyDefinition); - const stateMemory = getMethod(fakeMemoryKeyDefinition); + const stateDisk = activeSut.get(keyDefinitions.disk.userKeyDefinition); + const stateMemory = activeSut.get(keyDefinitions.memory.userKeyDefinition); expect(stateDisk).not.toStrictEqual(stateMemory); }); it("returns different instances when the state name differs", () => { - const state = getMethod(fakeDiskKeyDefinition); - const stateAlt = getMethod(fakeAlternateKeyDefinition); + const state = activeSut.get(keyDefinitions.disk.userKeyDefinition); + const stateAlt = activeSut.get(keyDefinitions.alternateDisk.userKeyDefinition); expect(state).not.toStrictEqual(stateAlt); }); it("returns different instances when the key differs", () => { - const state = getMethod(fakeDiskKeyDefinition); - const stateAlt = getMethod(fakeDiskKeyDefinitionAlternate); + const state = activeSut.get(keyDefinitions.disk.userKeyDefinition); + const stateAlt = activeSut.get(keyDefinitions.disk.altUserKeyDefinition); expect(state).not.toStrictEqual(stateAlt); }); }); - describe.each(globalAndSingle)("Global And Single Behavior", ({ getMethod }) => { - it("returns cached instance on repeated request", () => { - const stateFirst = getMethod(fakeDiskKeyDefinition); - const stateCached = getMethod(fakeDiskKeyDefinition); - expect(stateFirst).toStrictEqual(stateCached); - }); - }); + describe("single provider", () => { + it("returns a DefaultSingleUserState", () => { + const state = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); + + expect(state).toBeInstanceOf(DefaultSingleUserState); + }); + + it("returns different instances when the storage location differs", () => { + const stateDisk = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); + const stateMemory = singleSut.get(fakeUser1, keyDefinitions.memory.userKeyDefinition); + expect(stateDisk).not.toStrictEqual(stateMemory); + }); + + it("returns different instances when the state name differs", () => { + const state = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); + const stateAlt = singleSut.get(fakeUser1, keyDefinitions.alternateDisk.userKeyDefinition); + expect(state).not.toStrictEqual(stateAlt); + }); + + it("returns different instances when the key differs", () => { + const state = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); + const stateAlt = singleSut.get(fakeUser1, keyDefinitions.disk.altUserKeyDefinition); + expect(state).not.toStrictEqual(stateAlt); + }); - describe("DefaultSingleUserStateProvider only behavior", () => { const fakeUser2 = "00000000-0000-1000-a000-000000000002" as UserId; it("returns different instances when the user id differs", () => { - const user1State = singleSut.get(fakeUser1, fakeDiskKeyDefinition); - const user2State = singleSut.get(fakeUser2, fakeDiskKeyDefinition); + const user1State = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); + const user2State = singleSut.get(fakeUser2, keyDefinitions.disk.userKeyDefinition); expect(user1State).not.toStrictEqual(user2State); }); it("returns an instance with the userId property corresponding to the user id passed in", () => { - const userState = singleSut.get(fakeUser1, fakeDiskKeyDefinition); + const userState = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); expect(userState.userId).toBe(fakeUser1); }); + + it("returns cached instance on repeated request", () => { + const stateFirst = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); + const stateCached = singleSut.get(fakeUser1, keyDefinitions.disk.userKeyDefinition); + expect(stateFirst).toStrictEqual(stateCached); + }); + }); + + describe("global provider", () => { + it("returns a DefaultGlobalState", () => { + const state = globalSut.get(keyDefinitions.disk.keyDefinition); + + expect(state).toBeInstanceOf(DefaultGlobalState); + }); + + it("returns different instances when the storage location differs", () => { + const stateDisk = globalSut.get(keyDefinitions.disk.keyDefinition); + const stateMemory = globalSut.get(keyDefinitions.memory.keyDefinition); + expect(stateDisk).not.toStrictEqual(stateMemory); + }); + + it("returns different instances when the state name differs", () => { + const state = globalSut.get(keyDefinitions.disk.keyDefinition); + const stateAlt = globalSut.get(keyDefinitions.alternateDisk.keyDefinition); + expect(state).not.toStrictEqual(stateAlt); + }); + + it("returns different instances when the key differs", () => { + const state = globalSut.get(keyDefinitions.disk.keyDefinition); + const stateAlt = globalSut.get(keyDefinitions.disk.altKeyDefinition); + expect(state).not.toStrictEqual(stateAlt); + }); + + it("returns cached instance on repeated request", () => { + const stateFirst = globalSut.get(keyDefinitions.disk.keyDefinition); + const stateCached = globalSut.get(keyDefinitions.disk.keyDefinition); + expect(stateFirst).toStrictEqual(stateCached); + }); }); }); diff --git a/libs/common/src/platform/state/state.provider.ts b/libs/common/src/platform/state/state.provider.ts index a1e51552c7..44736500af 100644 --- a/libs/common/src/platform/state/state.provider.ts +++ b/libs/common/src/platform/state/state.provider.ts @@ -21,22 +21,6 @@ export abstract class StateProvider { /** @see{@link ActiveUserStateProvider.activeUserId$} */ abstract activeUserId$: Observable; - /** - * Gets a state observable for a given key and userId. - * - * @remarks If userId is falsy the observable returned will attempt to point to the currently active user _and not update if the active user changes_. - * This is different to how `getActive` works and more similar to `getUser` for whatever user happens to be active at the time of the call. - * If no user happens to be active at the time this method is called with a falsy userId then this observable will not emit a value until - * a user becomes active. If you are not confident a user is active at the time this method is called, you may want to pipe a call to `timeout` - * or instead call {@link getUserStateOrDefault$} and supply a value you would rather have given in the case of no passed in userId and no active user. - * - * @note consider converting your {@link KeyDefinition} to a {@link UserKeyDefinition} for additional features. - * - * @param keyDefinition - The key definition for the state you want to get. - * @param userId - The userId for which you want the state for. If not provided, the state for the currently active user will be returned. - */ - abstract getUserState$(keyDefinition: KeyDefinition, userId?: UserId): Observable; - /** * Gets a state observable for a given key and userId. * @@ -51,23 +35,6 @@ export abstract class StateProvider { */ abstract getUserState$(keyDefinition: UserKeyDefinition, userId?: UserId): Observable; - /** - * Gets a state observable for a given key and userId - * - * @remarks If userId is falsy the observable return will first attempt to point to the currently active user but will not follow subsequent active user changes, - * if there is no immediately available active user, then it will fallback to returning a default value in an observable that immediately completes. - * - * @note consider converting your {@link KeyDefinition} to a {@link UserKeyDefinition} for additional features. - * - * @param keyDefinition - The key definition for the state you want to get. - * @param config.userId - The userId for which you want the state for. If not provided, the state for the currently active user will be returned. - * @param config.defaultValue - The default value that should be wrapped in an observable if no active user is immediately available and no truthy userId is passed in. - */ - abstract getUserStateOrDefault$( - keyDefinition: KeyDefinition, - config: { userId: UserId | undefined; defaultValue?: T }, - ): Observable; - /** * Gets a state observable for a given key and userId * @@ -97,56 +64,11 @@ export abstract class StateProvider { userId?: UserId, ): Promise<[UserId, T]>; - /** - * Sets the state for a given key and userId. - * - * **NOTE** Consider converting your {@link KeyDefinition} to a {@link UserKeyDefinition} for additional features. - * - * @overload - * @param keyDefinition - The key definition for the state you want to set. - * @param value - The value to set the state to. - * @param userId - The userId for which you want to set the state for. If not provided, the state for the currently active user will be set. - */ - abstract setUserState( - keyDefinition: KeyDefinition, - value: T, - userId?: UserId, - ): Promise<[UserId, T]>; - - abstract setUserState( - keyDefinition: KeyDefinition | UserKeyDefinition, - value: T, - userId?: UserId, - ): Promise<[UserId, T]>; - /** @see{@link ActiveUserStateProvider.get} */ - abstract getActive(keyDefinition: UserKeyDefinition): ActiveUserState; - - /** - * @see{@link ActiveUserStateProvider.get} - * - * **NOTE** Consider converting your {@link KeyDefinition} to a {@link UserKeyDefinition} for additional features. - */ - abstract getActive(keyDefinition: KeyDefinition): ActiveUserState; - - /** @see{@link ActiveUserStateProvider.get} */ - abstract getActive(keyDefinition: KeyDefinition | UserKeyDefinition): ActiveUserState; + abstract getActive(userKeyDefinition: UserKeyDefinition): ActiveUserState; /** @see{@link SingleUserStateProvider.get} */ - abstract getUser(userId: UserId, keyDefinition: UserKeyDefinition): SingleUserState; - - /** - * @see{@link SingleUserStateProvider.get} - * - * **NOTE** Consider converting your {@link KeyDefinition} to a {@link UserKeyDefinition} for additional features. - */ - abstract getUser(userId: UserId, keyDefinition: KeyDefinition): SingleUserState; - - /** @see{@link SingleUserStateProvider.get} */ - abstract getUser( - userId: UserId, - keyDefinition: KeyDefinition | UserKeyDefinition, - ): SingleUserState; + abstract getUser(userId: UserId, userKeyDefinition: UserKeyDefinition): SingleUserState; /** @see{@link GlobalStateProvider.get} */ abstract getGlobal(keyDefinition: KeyDefinition): GlobalState; diff --git a/libs/common/src/platform/state/user-key-definition.ts b/libs/common/src/platform/state/user-key-definition.ts index 4c622e29f1..7e845fc858 100644 --- a/libs/common/src/platform/state/user-key-definition.ts +++ b/libs/common/src/platform/state/user-key-definition.ts @@ -3,7 +3,7 @@ import { StorageKey } from "../../types/state"; import { Utils } from "../misc/utils"; import { array, record } from "./deserialization-helpers"; -import { KeyDefinition, KeyDefinitionOptions } from "./key-definition"; +import { KeyDefinitionOptions } from "./key-definition"; import { StateDefinition } from "./state-definition"; export type ClearEvent = "lock" | "logout"; @@ -14,15 +14,6 @@ export type UserKeyDefinitionOptions = KeyDefinitionOptions & { const USER_KEY_DEFINITION_MARKER: unique symbol = Symbol("UserKeyDefinition"); -export function isUserKeyDefinition( - keyDefinition: KeyDefinition | UserKeyDefinition, -): keyDefinition is UserKeyDefinition { - return ( - USER_KEY_DEFINITION_MARKER in keyDefinition && - keyDefinition[USER_KEY_DEFINITION_MARKER] === true - ); -} - export class UserKeyDefinition { readonly [USER_KEY_DEFINITION_MARKER] = true; /** @@ -63,20 +54,6 @@ export class UserKeyDefinition { return this.options.cleanupDelayMs < 0 ? 0 : this.options.cleanupDelayMs ?? 1000; } - /** - * - * @param keyDefinition - * @returns - * - * @deprecated You should not use this to convert, just create a {@link UserKeyDefinition} - */ - static fromBaseKeyDefinition(keyDefinition: KeyDefinition) { - return new UserKeyDefinition(keyDefinition.stateDefinition, keyDefinition.key, { - ...keyDefinition["options"], - clearOn: [], // Default to not clearing - }); - } - /** * Creates a {@link UserKeyDefinition} for state that is an array. * @param stateDefinition The state definition to be added to the UserKeyDefinition diff --git a/libs/common/src/platform/state/user-state.provider.ts b/libs/common/src/platform/state/user-state.provider.ts index 3af10218f8..677f8b472d 100644 --- a/libs/common/src/platform/state/user-state.provider.ts +++ b/libs/common/src/platform/state/user-state.provider.ts @@ -2,22 +2,11 @@ import { Observable } from "rxjs"; import { UserId } from "../../types/guid"; -import { KeyDefinition } from "./key-definition"; import { UserKeyDefinition } from "./user-key-definition"; import { ActiveUserState, SingleUserState } from "./user-state"; /** A provider for getting an implementation of state scoped to a given key and userId */ export abstract class SingleUserStateProvider { - /** - * Gets a {@link SingleUserState} scoped to the given {@link KeyDefinition} and {@link UserId} - * - * **NOTE** Consider converting your {@link KeyDefinition} to a {@link UserKeyDefinition} for additional features. - * - * @param userId - The {@link UserId} for which you want the user state for. - * @param keyDefinition - The {@link KeyDefinition} for which you want the user state for. - */ - abstract get(userId: UserId, keyDefinition: KeyDefinition): SingleUserState; - /** * Gets a {@link SingleUserState} scoped to the given {@link UserKeyDefinition} and {@link UserId} * @@ -25,11 +14,6 @@ export abstract class SingleUserStateProvider { * @param userKeyDefinition - The {@link UserKeyDefinition} for which you want the user state for. */ abstract get(userId: UserId, userKeyDefinition: UserKeyDefinition): SingleUserState; - - abstract get( - userId: UserId, - keyDefinition: KeyDefinition | UserKeyDefinition, - ): SingleUserState; } /** A provider for getting an implementation of state scoped to a given key, but always pointing @@ -48,16 +32,4 @@ export abstract class ActiveUserStateProvider { * @param keyDefinition - The {@link UserKeyDefinition} for which you want the user state for. */ abstract get(userKeyDefinition: UserKeyDefinition): ActiveUserState; - - /** - * Gets a {@link ActiveUserState} scoped to the given {@link KeyDefinition}, but updates when active user changes such - * that the emitted values always represents the state for the currently active user. - * - * **NOTE** Consider converting your {@link KeyDefinition} to a {@link UserKeyDefinition} for additional features. - * - * @param keyDefinition - The {@link KeyDefinition} for which you want the user state for. - */ - abstract get(keyDefinition: KeyDefinition): ActiveUserState; - - abstract get(keyDefinition: KeyDefinition | UserKeyDefinition): ActiveUserState; }