Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 0109e34

Browse files
committed
Document and support the established naming convention for config opts
This change: * Rename `ConfigOptions` to `IConfigOptions` to match code convention/style, plus move it to a dedicated file * Update comments and surrounding documentation * Define every single documented option (from element-web's config.md) * Enable a linter to enforce the convention * Invent a translation layer for a different change to use * No attempt to fix build errors from doing this (at this stage)
1 parent e1fdff4 commit 0109e34

File tree

7 files changed

+289
-39
lines changed

7 files changed

+289
-39
lines changed

src/@types/common.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,10 @@ export type RecursivePartial<T> = {
4141
T[P] extends object ? RecursivePartial<T[P]> :
4242
T[P];
4343
};
44+
45+
// Condensed but also expanded version of https://stackoverflow.com/a/60206860
46+
export type KeysOfStrictType<Input, SearchType> = {
47+
[P in keyof Input]: Input[P] extends SearchType
48+
? (SearchType extends Input[P] ? P : never)
49+
: never;
50+
}[keyof Input];

src/@types/global.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import { ConsoleLogger, IndexedDBLogStore } from "../rageshake/rageshake";
5252
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
5353
import { Skinner } from "../Skinner";
5454
import AutoRageshakeStore from "../stores/AutoRageshakeStore";
55-
import { ConfigOptions } from "../SdkConfig";
55+
import { IConfigOptions } from "../IConfigOptions";
5656

5757
/* eslint-disable @typescript-eslint/naming-convention */
5858

@@ -63,7 +63,7 @@ declare global {
6363
Olm: {
6464
init: () => Promise<void>;
6565
};
66-
mxReactSdkConfig: ConfigOptions;
66+
mxReactSdkConfig: IConfigOptions;
6767

6868
// Needed for Safari, unknown to TypeScript
6969
webkitAudioContext: typeof AudioContext;

src/IConfigOptions.ts

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
Copyright 2016 OpenMarket Ltd
3+
Copyright 2019 - 2022 The Matrix.org Foundation C.I.C.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
19+
20+
// Convention decision: All config options are lower_snake_case
21+
// We use an isolated file for the interface so we can mess around with the eslint options.
22+
23+
/* eslint-disable camelcase */
24+
/* eslint @typescript-eslint/naming-convention: ["error", { "selector": "property", "format": ["snake_case"] } ] */
25+
26+
// see element-web config.md for non-developer docs
27+
export interface IConfigOptions {
28+
// dev note: while true that this is arbitrary JSON, it's valuable to enforce that all
29+
// config options are documented for "find all usages" sort of searching.
30+
// [key: string]: any;
31+
32+
// Properties of this interface are roughly grouped by their subject matter, such as
33+
// "instance customisation", "login stuff", "branding", etc. Use blank lines to denote
34+
// a logical separation of properties, but keep similar ones near each other.
35+
36+
// Exactly one of the following must be supplied
37+
default_server_config?: IClientWellKnown; // copy/paste of client well-known
38+
default_server_name?: string; // domain to do well-known lookup on
39+
default_hs_url?: string; // http url
40+
41+
default_is_url?: string; // used in combination with default_hs_url, but for the identity server
42+
43+
disable_custom_urls?: boolean;
44+
disable_guests?: boolean;
45+
disable_login_language_selector?: boolean;
46+
disable_3pid_login?: boolean;
47+
48+
brand?: string;
49+
branding?: {
50+
welcome_background_url?: string;
51+
auth_header_logo_url?: string;
52+
auth_footer_links?: {text: string, url: string}[];
53+
};
54+
55+
map_style_url?: string; // for location-shared maps
56+
57+
embedded_pages?: {
58+
welcome_url?: string;
59+
home_url?: string;
60+
login_for_welcome?: boolean;
61+
};
62+
63+
permalink_prefix?: string;
64+
65+
update_base_url?: string;
66+
desktop_builds?: {
67+
available: boolean;
68+
logo: string; // url
69+
url: string; // download url
70+
};
71+
mobile_builds?: {
72+
ios?: string; // download url
73+
android?: string; // download url
74+
fdroid?: string; // download url
75+
};
76+
77+
mobile_guide_toast?: boolean;
78+
79+
default_theme?: "light" | "dark" | string; // custom themes are strings
80+
default_country_code?: string; // ISO 3166 alpha2 country code
81+
default_federate?: boolean;
82+
default_device_display_name?: string; // for device naming on login+registration
83+
84+
setting_defaults?: Record<string, any>; // <SettingName, Value>
85+
86+
integrations_ui_url?: string;
87+
integrations_rest_url?: string;
88+
integrations_widgets_urls?: string[];
89+
90+
show_labs_settings?: boolean;
91+
features?: Record<string, boolean>; // <FeatureName, EnabledBool>
92+
93+
bug_report_endpoint_url?: string; // omission disables bug reporting
94+
uisi_autorageshake_app?: string;
95+
sentry?: {
96+
dsn: string;
97+
environment?: string; // "production", etc
98+
};
99+
100+
audio_stream_url?: string;
101+
jitsi?: {
102+
preferred_domain: string;
103+
};
104+
voip?: {
105+
obey_asserted_identity?: boolean; // MSC3086
106+
};
107+
108+
logout_redirect_url?: string;
109+
110+
// sso_immediate_redirect is deprecated in favour of sso_redirect_options.immediate
111+
sso_immediate_redirect?: boolean;
112+
sso_redirect_options?: ISsoRedirectOptions;
113+
114+
custom_translations_url?: string;
115+
116+
report_event?: {
117+
admin_message_md: string; // message for how to contact the server owner when reporting an event
118+
};
119+
120+
welcome_user_id?: string;
121+
122+
room_directory?: {
123+
servers: string[];
124+
};
125+
126+
// piwik (matomo) is deprecated in favour of posthog
127+
piwik?: false | {
128+
url: string; // piwik instance
129+
site_id: string | number; // TODO: @@TR Typed correctly?
130+
policy_url: string; // cookie policy
131+
whitelisted_hs_urls: string[];
132+
};
133+
posthog?: {
134+
project_api_key: string;
135+
api_host: string; // hostname
136+
};
137+
analytics_owner?: string; // defaults to `brand`
138+
}
139+
140+
export interface ISsoRedirectOptions {
141+
immediate?: boolean;
142+
on_welcome_page?: boolean;
143+
}

src/SdkConfig.ts

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
Copyright 2016 OpenMarket Ltd
3-
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
3+
Copyright 2019 - 2022 The Matrix.org Foundation C.I.C.
44
55
Licensed under the Apache License, Version 2.0 (the "License");
66
you may not use this file except in compliance with the License.
@@ -15,61 +15,62 @@ See the License for the specific language governing permissions and
1515
limitations under the License.
1616
*/
1717

18-
export interface ISsoRedirectOptions {
19-
immediate?: boolean;
20-
on_welcome_page?: boolean; // eslint-disable-line camelcase
21-
}
22-
23-
/* eslint-disable camelcase */
24-
export interface ConfigOptions {
25-
[key: string]: any;
26-
27-
logout_redirect_url?: string;
18+
import { Optional } from "matrix-events-sdk";
2819

29-
// sso_immediate_redirect is deprecated in favour of sso_redirect_options.immediate
30-
sso_immediate_redirect?: boolean;
31-
sso_redirect_options?: ISsoRedirectOptions;
32-
33-
custom_translations_url?: string;
34-
}
35-
/* eslint-enable camelcase*/
20+
import { SnakedObject } from "./utils/SnakedObject";
21+
import { IConfigOptions, ISsoRedirectOptions } from "./IConfigOptions";
22+
import { KeysOfStrictType } from "./@types/common";
3623

37-
export const DEFAULTS: ConfigOptions = {
38-
// Brand name of the app
24+
// see element-web config.md for docs, or the IConfigOptions interface for dev docs
25+
export const DEFAULTS: Partial<IConfigOptions> = {
3926
brand: "Element",
40-
// URL to a page we show in an iframe to configure integrations
4127
integrations_ui_url: "https://scalar.vector.im/",
42-
// Base URL to the REST interface of the integrations server
4328
integrations_rest_url: "https://scalar.vector.im/api",
44-
// Where to send bug reports. If not specified, bugs cannot be sent.
4529
bug_report_endpoint_url: null,
46-
// Jitsi conference options
4730
jitsi: {
48-
// Default conference domain
49-
preferredDomain: "meet.element.io",
31+
preferred_domain: "meet.element.io",
5032
},
51-
desktopBuilds: {
33+
desktop_builds: {
5234
available: true,
5335
logo: require("../res/img/element-desktop-logo.svg").default,
5436
url: "https://element.io/get-started",
5537
},
5638
};
5739

5840
export default class SdkConfig {
59-
private static instance: ConfigOptions;
41+
private static instance: IConfigOptions;
42+
private static fallback: SnakedObject<IConfigOptions>;
6043

61-
private static setInstance(i: ConfigOptions) {
44+
private static setInstance(i: IConfigOptions) {
6245
SdkConfig.instance = i;
46+
SdkConfig.fallback = new SnakedObject(i);
6347

6448
// For debugging purposes
6549
window.mxReactSdkConfig = i;
6650
}
6751

68-
public static get() {
69-
return SdkConfig.instance || {};
52+
public static get(): IConfigOptions;
53+
public static get<K extends keyof IConfigOptions>(key: K, altCaseName?: string): IConfigOptions[K];
54+
public static get<K extends keyof IConfigOptions = never>(
55+
key?: K, altCaseName?: string,
56+
): IConfigOptions | IConfigOptions[K] {
57+
if (key === undefined) return SdkConfig.instance || {};
58+
return SdkConfig.fallback.get(key, altCaseName);
59+
}
60+
61+
public static getObject<K extends KeysOfStrictType<IConfigOptions, object>>(
62+
key: K, altCaseName?: string,
63+
): Optional<SnakedObject<IConfigOptions[K]>> {
64+
const val = SdkConfig.get(key, altCaseName);
65+
if (val !== null && val !== undefined) {
66+
return new SnakedObject(val);
67+
}
68+
69+
// return the same type for sensitive callers (some want `undefined` specifically)
70+
return val === undefined ? undefined : null;
7071
}
7172

72-
public static put(cfg: ConfigOptions) {
73+
public static put(cfg: IConfigOptions) {
7374
const defaultKeys = Object.keys(DEFAULTS);
7475
for (let i = 0; i < defaultKeys.length; ++i) {
7576
if (cfg[defaultKeys[i]] === undefined) {
@@ -83,14 +84,14 @@ export default class SdkConfig {
8384
SdkConfig.setInstance({});
8485
}
8586

86-
public static add(cfg: ConfigOptions) {
87+
public static add(cfg: IConfigOptions) {
8788
const liveConfig = SdkConfig.get();
8889
const newConfig = Object.assign({}, liveConfig, cfg);
8990
SdkConfig.put(newConfig);
9091
}
9192
}
9293

93-
export function parseSsoRedirectOptions(config: ConfigOptions): ISsoRedirectOptions {
94+
export function parseSsoRedirectOptions(config: IConfigOptions): ISsoRedirectOptions {
9495
// Ignore deprecated options if the config is using new ones
9596
if (config.sso_redirect_options) return config.sso_redirect_options;
9697

src/utils/SnakedObject.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
export function snakeToCamel(s: string): string {
18+
return s.replace(/._./g, v => `${v[0]}${v[2].toUpperCase()}`);
19+
}
20+
21+
export class SnakedObject<T = Record<string, any>> {
22+
public constructor(private obj: T) {
23+
}
24+
25+
public get<K extends string & keyof T>(key: K, altCaseName?: string): T[K] {
26+
const val = this.obj[key];
27+
if (val !== undefined) return val;
28+
29+
return this.obj[altCaseName ?? snakeToCamel(key)];
30+
}
31+
32+
// Make JSON.stringify() pretend that everything is fine
33+
public toJSON() {
34+
return this.obj;
35+
}
36+
}

src/utils/pages.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { ConfigOptions } from "../SdkConfig";
17+
import { IConfigOptions } from "../IConfigOptions";
1818

19-
export function getHomePageUrl(appConfig: ConfigOptions): string | null {
19+
export function getHomePageUrl(appConfig: IConfigOptions): string | null {
2020
const pagesConfig = appConfig.embeddedPages;
2121
let pageUrl = pagesConfig?.homeUrl;
2222

@@ -30,7 +30,7 @@ export function getHomePageUrl(appConfig: ConfigOptions): string | null {
3030
return pageUrl;
3131
}
3232

33-
export function shouldUseLoginForWelcome(appConfig: ConfigOptions): boolean {
33+
export function shouldUseLoginForWelcome(appConfig: IConfigOptions): boolean {
3434
const pagesConfig = appConfig.embeddedPages;
3535
return pagesConfig?.loginForWelcome === true;
3636
}

0 commit comments

Comments
 (0)