mirror of
https://github.com/bitwarden/browser.git
synced 2025-03-18 14:39:21 +01:00
Split session key and synced item property key (#3317)
This commit is contained in:
parent
16c41b823b
commit
7c3facec80
apps/browser/src/decorators/session-sync-observable
@ -39,7 +39,7 @@ export function browserSession<TCtor extends Constructor<any>>(constructor: TCto
|
||||
}
|
||||
|
||||
buildSyncer(metadata: SyncedItemMetadata, stateService: StateService) {
|
||||
const syncer = new SessionSyncer((this as any)[metadata.key], stateService, metadata);
|
||||
const syncer = new SessionSyncer((this as any)[metadata.propertyKey], stateService, metadata);
|
||||
syncer.init();
|
||||
return syncer;
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ describe("sessionSync decorator", () => {
|
||||
const testClass = new TestClass();
|
||||
expect((testClass as any).__syncedItemMetadata).toEqual([
|
||||
expect.objectContaining({
|
||||
key: "TestClass_testProperty",
|
||||
propertyKey: "testProperty",
|
||||
sessionKey: "TestClass_testProperty",
|
||||
ctor: ctor,
|
||||
initializer: initializer,
|
||||
}),
|
||||
|
@ -42,7 +42,8 @@ export function sessionSync<T>(buildOptions: BuildOptions<T>) {
|
||||
}
|
||||
|
||||
p.__syncedItemMetadata.push({
|
||||
key: `${prototype.constructor.name}_${propertyKey}`,
|
||||
propertyKey,
|
||||
sessionKey: `${prototype.constructor.name}_${propertyKey}`,
|
||||
ctor: buildOptions.ctor,
|
||||
initializer: buildOptions.initializer,
|
||||
initializeAsArray: buildOptions.initializeAsArray,
|
||||
|
@ -7,8 +7,9 @@ import { StateService } from "../../services/abstractions/state.service";
|
||||
import { SessionSyncer } from "./session-syncer";
|
||||
|
||||
describe("session syncer", () => {
|
||||
const key = "Test__behaviorSubject";
|
||||
const metaData = { key, initializer: (s: string) => s };
|
||||
const propertyKey = "behaviorSubject";
|
||||
const sessionKey = "Test__" + propertyKey;
|
||||
const metaData = { propertyKey, sessionKey, initializer: (s: string) => s };
|
||||
let stateService: MockProxy<StateService>;
|
||||
let sut: SessionSyncer;
|
||||
let behaviorSubject: BehaviorSubject<string>;
|
||||
@ -40,18 +41,19 @@ describe("session syncer", () => {
|
||||
|
||||
it("should create if either ctor or initializer is provided", () => {
|
||||
expect(
|
||||
new SessionSyncer(behaviorSubject, stateService, { key: key, ctor: String })
|
||||
new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey, ctor: String })
|
||||
).toBeDefined();
|
||||
expect(
|
||||
new SessionSyncer(behaviorSubject, stateService, {
|
||||
key: key,
|
||||
propertyKey,
|
||||
sessionKey,
|
||||
initializer: (s: any) => s,
|
||||
})
|
||||
).toBeDefined();
|
||||
});
|
||||
it("should throw if neither ctor or initializer is provided", () => {
|
||||
expect(() => {
|
||||
new SessionSyncer(behaviorSubject, stateService, { key: key });
|
||||
new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey });
|
||||
}).toThrowError("ctor or initializer must be provided");
|
||||
});
|
||||
});
|
||||
@ -96,7 +98,7 @@ describe("session syncer", () => {
|
||||
// await finishing of fire-and-forget operation
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
expect(stateService.setInSessionMemory).toHaveBeenCalledTimes(1);
|
||||
expect(stateService.setInSessionMemory).toHaveBeenCalledWith(key, "test");
|
||||
expect(stateService.setInSessionMemory).toHaveBeenCalledWith(sessionKey, "test");
|
||||
});
|
||||
|
||||
it("should update sessionSyncers in other contexts", async () => {
|
||||
@ -104,7 +106,7 @@ describe("session syncer", () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(sendMessageSpy).toHaveBeenCalledWith(`${key}_update`, { id: sut.id });
|
||||
expect(sendMessageSpy).toHaveBeenCalledWith(`${sessionKey}_update`, { id: sut.id });
|
||||
});
|
||||
});
|
||||
|
||||
@ -131,7 +133,7 @@ describe("session syncer", () => {
|
||||
});
|
||||
|
||||
it("should ignore messages from itself", async () => {
|
||||
await sut.updateFromMessage({ command: `${key}_update`, id: sut.id });
|
||||
await sut.updateFromMessage({ command: `${sessionKey}_update`, id: sut.id });
|
||||
|
||||
expect(stateService.getFromSessionMemory).not.toHaveBeenCalled();
|
||||
expect(nextSpy).not.toHaveBeenCalled();
|
||||
@ -140,10 +142,10 @@ describe("session syncer", () => {
|
||||
it("should update from message on emit from another instance", async () => {
|
||||
stateService.getFromSessionMemory.mockResolvedValue("test");
|
||||
|
||||
await sut.updateFromMessage({ command: `${key}_update`, id: "different_id" });
|
||||
await sut.updateFromMessage({ command: `${sessionKey}_update`, id: "different_id" });
|
||||
|
||||
expect(stateService.getFromSessionMemory).toHaveBeenCalledTimes(1);
|
||||
expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(key);
|
||||
expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(sessionKey);
|
||||
|
||||
expect(nextSpy).toHaveBeenCalledTimes(1);
|
||||
expect(nextSpy).toHaveBeenCalledWith("test");
|
||||
|
@ -62,18 +62,18 @@ export class SessionSyncer {
|
||||
if (message.command != this.updateMessageCommand || message.id === this.id) {
|
||||
return;
|
||||
}
|
||||
const keyValuePair = await this.stateService.getFromSessionMemory(this.metaData.key);
|
||||
const keyValuePair = await this.stateService.getFromSessionMemory(this.metaData.sessionKey);
|
||||
const value = SyncedItemMetadata.buildFromKeyValuePair(keyValuePair, this.metaData);
|
||||
this.ignoreNextUpdate = true;
|
||||
this.behaviorSubject.next(value);
|
||||
}
|
||||
|
||||
private async updateSession(value: any) {
|
||||
await this.stateService.setInSessionMemory(this.metaData.key, value);
|
||||
await this.stateService.setInSessionMemory(this.metaData.sessionKey, value);
|
||||
await BrowserApi.sendMessage(this.updateMessageCommand, { id: this.id });
|
||||
}
|
||||
|
||||
private get updateMessageCommand() {
|
||||
return `${this.metaData.key}_update`;
|
||||
return `${this.metaData.sessionKey}_update`;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
export class SyncedItemMetadata {
|
||||
key: string;
|
||||
propertyKey: string;
|
||||
sessionKey: string;
|
||||
ctor?: new () => any;
|
||||
initializer?: (keyValuePair: any) => any;
|
||||
initializeAsArray?: boolean;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { SyncedItemMetadata } from "./sync-item-metadata";
|
||||
|
||||
describe("build from key value pair", () => {
|
||||
const propertyKey = "propertyKey";
|
||||
const key = "key";
|
||||
const initializer = (s: any) => "used initializer";
|
||||
class TestClass {}
|
||||
@ -10,7 +11,8 @@ describe("build from key value pair", () => {
|
||||
const actual = SyncedItemMetadata.buildFromKeyValuePair(
|
||||
{},
|
||||
{
|
||||
key: "key",
|
||||
propertyKey,
|
||||
sessionKey: "key",
|
||||
initializer: initializer,
|
||||
}
|
||||
);
|
||||
@ -21,7 +23,8 @@ describe("build from key value pair", () => {
|
||||
it("should call ctor if provided", () => {
|
||||
const expected = { provided: "value" };
|
||||
const actual = SyncedItemMetadata.buildFromKeyValuePair(expected, {
|
||||
key: key,
|
||||
propertyKey,
|
||||
sessionKey: key,
|
||||
ctor: ctor,
|
||||
});
|
||||
|
||||
@ -33,7 +36,8 @@ describe("build from key value pair", () => {
|
||||
const actual = SyncedItemMetadata.buildFromKeyValuePair(
|
||||
{},
|
||||
{
|
||||
key: key,
|
||||
propertyKey,
|
||||
sessionKey: key,
|
||||
initializer: initializer,
|
||||
ctor: ctor,
|
||||
}
|
||||
@ -44,7 +48,8 @@ describe("build from key value pair", () => {
|
||||
|
||||
it("should honor initialize as array", () => {
|
||||
const actual = SyncedItemMetadata.buildFromKeyValuePair([1, 2], {
|
||||
key: key,
|
||||
propertyKey,
|
||||
sessionKey: key,
|
||||
initializer: initializer,
|
||||
initializeAsArray: true,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user