Store refactor: convert WidgetPermissionStore (#9458)

* Store refactor: convert WidgetPermissionStore

Add Jest tests as well.

* More tests

* Review comments
This commit is contained in:
kegsay 2022-10-19 21:00:53 +01:00 committed by GitHub
parent 7d0af1dca4
commit 17c3fb89c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 129 additions and 17 deletions

View file

@ -21,10 +21,11 @@ import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import { OIDCState, WidgetPermissionStore } from "../../../stores/widgets/WidgetPermissionStore"; import { OIDCState } from "../../../stores/widgets/WidgetPermissionStore";
import { IDialogProps } from "./IDialogProps"; import { IDialogProps } from "./IDialogProps";
import BaseDialog from "./BaseDialog"; import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons"; import DialogButtons from "../elements/DialogButtons";
import { SdkContextClass } from '../../../contexts/SDKContext';
interface IProps extends IDialogProps { interface IProps extends IDialogProps {
widget: Widget; widget: Widget;
@ -57,7 +58,7 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
if (this.state.rememberSelection) { if (this.state.rememberSelection) {
logger.log(`Remembering ${this.props.widget.id} as allowed=${allowed} for OpenID`); logger.log(`Remembering ${this.props.widget.id} as allowed=${allowed} for OpenID`);
WidgetPermissionStore.instance.setOIDCState( SdkContextClass.instance.widgetPermissionStore.setOIDCState(
this.props.widget, this.props.widgetKind, this.props.inRoomId, this.props.widget, this.props.widgetKind, this.props.inRoomId,
allowed ? OIDCState.Allowed : OIDCState.Denied, allowed ? OIDCState.Allowed : OIDCState.Denied,
); );

View file

@ -27,6 +27,7 @@ import { RoomViewStore } from "../stores/RoomViewStore";
import SpaceStore, { SpaceStoreClass } from "../stores/spaces/SpaceStore"; import SpaceStore, { SpaceStoreClass } from "../stores/spaces/SpaceStore";
import TypingStore from "../stores/TypingStore"; import TypingStore from "../stores/TypingStore";
import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore"; import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
import { WidgetPermissionStore } from "../stores/widgets/WidgetPermissionStore";
import WidgetStore from "../stores/WidgetStore"; import WidgetStore from "../stores/WidgetStore";
export const SDKContext = createContext<SdkContextClass>(undefined); export const SDKContext = createContext<SdkContextClass>(undefined);
@ -51,6 +52,7 @@ export class SdkContextClass {
public client?: MatrixClient; public client?: MatrixClient;
// All protected fields to make it easier to derive test stores // All protected fields to make it easier to derive test stores
protected _WidgetPermissionStore?: WidgetPermissionStore;
protected _RightPanelStore?: RightPanelStore; protected _RightPanelStore?: RightPanelStore;
protected _RoomNotificationStateStore?: RoomNotificationStateStore; protected _RoomNotificationStateStore?: RoomNotificationStateStore;
protected _RoomViewStore?: RoomViewStore; protected _RoomViewStore?: RoomViewStore;
@ -102,6 +104,12 @@ export class SdkContextClass {
} }
return this._WidgetLayoutStore; return this._WidgetLayoutStore;
} }
public get widgetPermissionStore(): WidgetPermissionStore {
if (!this._WidgetPermissionStore) {
this._WidgetPermissionStore = new WidgetPermissionStore(this);
}
return this._WidgetPermissionStore;
}
public get widgetStore(): WidgetStore { public get widgetStore(): WidgetStore {
if (!this._WidgetStore) { if (!this._WidgetStore) {
this._WidgetStore = WidgetStore.instance; this._WidgetStore = WidgetStore.instance;

View file

@ -47,7 +47,7 @@ import Modal from "../../Modal";
import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog"; import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog";
import WidgetCapabilitiesPromptDialog from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog"; import WidgetCapabilitiesPromptDialog from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog";
import { WidgetPermissionCustomisations } from "../../customisations/WidgetPermissions"; import { WidgetPermissionCustomisations } from "../../customisations/WidgetPermissions";
import { OIDCState, WidgetPermissionStore } from "./WidgetPermissionStore"; import { OIDCState } from "./WidgetPermissionStore";
import { WidgetType } from "../../widgets/WidgetType"; import { WidgetType } from "../../widgets/WidgetType";
import { CHAT_EFFECTS } from "../../effects"; import { CHAT_EFFECTS } from "../../effects";
import { containsEmoji } from "../../effects/utils"; import { containsEmoji } from "../../effects/utils";
@ -350,7 +350,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
} }
public async askOpenID(observer: SimpleObservable<IOpenIDUpdate>) { public async askOpenID(observer: SimpleObservable<IOpenIDUpdate>) {
const oidcState = WidgetPermissionStore.instance.getOIDCState( const oidcState = SdkContextClass.instance.widgetPermissionStore.getOIDCState(
this.forWidget, this.forWidgetKind, this.inRoomId, this.forWidget, this.forWidgetKind, this.inRoomId,
); );

View file

@ -17,8 +17,8 @@
import { Widget, WidgetKind } from "matrix-widget-api"; import { Widget, WidgetKind } from "matrix-widget-api";
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import { SettingLevel } from "../../settings/SettingLevel"; import { SettingLevel } from "../../settings/SettingLevel";
import { SdkContextClass } from "../../contexts/SDKContext";
export enum OIDCState { export enum OIDCState {
Allowed, // user has set the remembered value as allowed Allowed, // user has set the remembered value as allowed
@ -27,16 +27,7 @@ export enum OIDCState {
} }
export class WidgetPermissionStore { export class WidgetPermissionStore {
private static internalInstance: WidgetPermissionStore; public constructor(private readonly context: SdkContextClass) {
private constructor() {
}
public static get instance(): WidgetPermissionStore {
if (!WidgetPermissionStore.internalInstance) {
WidgetPermissionStore.internalInstance = new WidgetPermissionStore();
}
return WidgetPermissionStore.internalInstance;
} }
// TODO (all functions here): Merge widgetKind with the widget definition // TODO (all functions here): Merge widgetKind with the widget definition
@ -44,7 +35,7 @@ export class WidgetPermissionStore {
private packSettingKey(widget: Widget, kind: WidgetKind, roomId?: string): string { private packSettingKey(widget: Widget, kind: WidgetKind, roomId?: string): string {
let location = roomId; let location = roomId;
if (kind !== WidgetKind.Room) { if (kind !== WidgetKind.Room) {
location = MatrixClientPeg.get().getUserId(); location = this.context.client?.getUserId();
} }
if (kind === WidgetKind.Modal) { if (kind === WidgetKind.Modal) {
location = '*MODAL*-' + location; // to guarantee differentiation from whatever spawned it location = '*MODAL*-' + location; // to guarantee differentiation from whatever spawned it
@ -71,7 +62,10 @@ export class WidgetPermissionStore {
public setOIDCState(widget: Widget, kind: WidgetKind, roomId: string, newState: OIDCState) { public setOIDCState(widget: Widget, kind: WidgetKind, roomId: string, newState: OIDCState) {
const settingsKey = this.packSettingKey(widget, kind, roomId); const settingsKey = this.packSettingKey(widget, kind, roomId);
const currentValues = SettingsStore.getValue("widgetOpenIDPermissions"); let currentValues = SettingsStore.getValue("widgetOpenIDPermissions");
if (!currentValues) {
currentValues = {};
}
if (!currentValues.allow) currentValues.allow = []; if (!currentValues.allow) currentValues.allow = [];
if (!currentValues.deny) currentValues.deny = []; if (!currentValues.deny) currentValues.deny = [];

View file

@ -22,6 +22,7 @@ import RightPanelStore from "../src/stores/right-panel/RightPanelStore";
import { RoomViewStore } from "../src/stores/RoomViewStore"; import { RoomViewStore } from "../src/stores/RoomViewStore";
import { SpaceStoreClass } from "../src/stores/spaces/SpaceStore"; import { SpaceStoreClass } from "../src/stores/spaces/SpaceStore";
import { WidgetLayoutStore } from "../src/stores/widgets/WidgetLayoutStore"; import { WidgetLayoutStore } from "../src/stores/widgets/WidgetLayoutStore";
import { WidgetPermissionStore } from "../src/stores/widgets/WidgetPermissionStore";
import WidgetStore from "../src/stores/WidgetStore"; import WidgetStore from "../src/stores/WidgetStore";
/** /**
@ -32,6 +33,7 @@ export class TestSdkContext extends SdkContextClass {
public _RightPanelStore?: RightPanelStore; public _RightPanelStore?: RightPanelStore;
public _RoomNotificationStateStore?: RoomNotificationStateStore; public _RoomNotificationStateStore?: RoomNotificationStateStore;
public _RoomViewStore?: RoomViewStore; public _RoomViewStore?: RoomViewStore;
public _WidgetPermissionStore?: WidgetPermissionStore;
public _WidgetLayoutStore?: WidgetLayoutStore; public _WidgetLayoutStore?: WidgetLayoutStore;
public _WidgetStore?: WidgetStore; public _WidgetStore?: WidgetStore;
public _PosthogAnalytics?: PosthogAnalytics; public _PosthogAnalytics?: PosthogAnalytics;

View file

@ -0,0 +1,107 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { mocked } from "jest-mock";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { Widget, WidgetKind } from "matrix-widget-api";
import { OIDCState, WidgetPermissionStore } from "../../../src/stores/widgets/WidgetPermissionStore";
import SettingsStore from "../../../src/settings/SettingsStore";
import { TestSdkContext } from "../../TestSdkContext";
import { SettingLevel } from "../../../src/settings/SettingLevel";
import { SdkContextClass } from "../../../src/contexts/SDKContext";
import { stubClient } from "../../test-utils";
jest.mock("../../../src/settings/SettingsStore");
describe("WidgetPermissionStore", () => {
let widgetPermissionStore: WidgetPermissionStore;
let mockClient: MatrixClient;
const userId = "@alice:localhost";
const roomId = "!room:localhost";
const w = new Widget({
id: "wid",
creatorUserId: userId,
type: "m.custom",
url: "https://invalid.address.here",
});
let settings = {}; // key value store
beforeEach(() => {
settings = {}; // clear settings
mocked(SettingsStore.getValue).mockImplementation((setting: string) => {
return settings[setting];
});
mocked(SettingsStore.setValue).mockImplementation((settingName: string,
roomId: string | null,
level: SettingLevel,
value: any,
): Promise<void> => {
// the store doesn't use any specific level or room ID (room IDs are packed into keys in `value`)
settings[settingName] = value;
return Promise.resolve();
});
mockClient = stubClient();
const context = new TestSdkContext();
context.client = mockClient;
widgetPermissionStore = new WidgetPermissionStore(context);
});
it("should persist OIDCState.Allowed for a widget", () => {
widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed);
// check it remembered the value
expect(
widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null),
).toEqual(OIDCState.Allowed);
});
it("should persist OIDCState.Denied for a widget", () => {
widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied);
// check it remembered the value
expect(
widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null),
).toEqual(OIDCState.Denied);
});
it("should update OIDCState for a widget", () => {
widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed);
widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied);
// check it remembered the latest value
expect(
widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null),
).toEqual(OIDCState.Denied);
});
it("should scope the location for a widget when setting OIDC state", () => {
// allow this widget for this room
widgetPermissionStore.setOIDCState(w, WidgetKind.Room, roomId, OIDCState.Allowed);
// check it remembered the value
expect(
widgetPermissionStore.getOIDCState(w, WidgetKind.Room, roomId),
).toEqual(OIDCState.Allowed);
// check this is not the case for the entire account
expect(
widgetPermissionStore.getOIDCState(w, WidgetKind.Account, roomId),
).toEqual(OIDCState.Unknown);
});
it("is created once in SdkContextClass", () => {
const context = new SdkContextClass();
const store = context.widgetPermissionStore;
expect(store).toBeDefined();
const store2 = context.widgetPermissionStore;
expect(store2).toStrictEqual(store);
});
});