From a35735da45322570d3cb4edd66b54e50c59beda8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Aug 2019 14:49:20 -0600 Subject: [PATCH 1/3] Support homeserver-configured integration managers Fixes https://github.com/vector-im/riot-web/issues/4913 Requires https://github.com/matrix-org/matrix-js-sdk/pull/1024 Implements part of [MSC1957](https://github.com/matrix-org/matrix-doc/pull/1957) --- .../IntegrationManagerInstance.js | 2 + src/integrations/IntegrationManagers.js | 72 ++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/integrations/IntegrationManagerInstance.js b/src/integrations/IntegrationManagerInstance.js index 6744d82e75..d36fa73d48 100644 --- a/src/integrations/IntegrationManagerInstance.js +++ b/src/integrations/IntegrationManagerInstance.js @@ -23,11 +23,13 @@ import url from 'url'; export const KIND_ACCOUNT = "account"; export const KIND_CONFIG = "config"; +export const KIND_HOMESERVER = "homeserver"; export class IntegrationManagerInstance { apiUrl: string; uiUrl: string; kind: string; + id: string; // only applicable in some cases constructor(kind: string, apiUrl: string, uiUrl: string) { this.kind = kind; diff --git a/src/integrations/IntegrationManagers.js b/src/integrations/IntegrationManagers.js index 0e19c7add0..52486bd30e 100644 --- a/src/integrations/IntegrationManagers.js +++ b/src/integrations/IntegrationManagers.js @@ -17,10 +17,19 @@ limitations under the License. import SdkConfig from '../SdkConfig'; import sdk from "../index"; import Modal from '../Modal'; -import {IntegrationManagerInstance, KIND_ACCOUNT, KIND_CONFIG} from "./IntegrationManagerInstance"; +import {IntegrationManagerInstance, KIND_ACCOUNT, KIND_CONFIG, KIND_HOMESERVER} from "./IntegrationManagerInstance"; import type {MatrixClient, MatrixEvent} from "matrix-js-sdk"; import WidgetUtils from "../utils/WidgetUtils"; import MatrixClientPeg from "../MatrixClientPeg"; +import {AutoDiscovery} from "../../../matrix-js-sdk"; + +const HS_MANAGERS_REFRESH_INTERVAL = 8 * 60 * 60 * 1000; // 8 hours +const KIND_PREFERENCE = [ + // Ordered: first is most preferred, last is least preferred. + KIND_ACCOUNT, + KIND_HOMESERVER, + KIND_CONFIG, +]; export class IntegrationManagers { static _instance; @@ -34,6 +43,8 @@ export class IntegrationManagers { _managers: IntegrationManagerInstance[] = []; _client: MatrixClient; + _wellknownRefreshTimerId: number = null; + _primaryManager: IntegrationManagerInstance; constructor() { this._compileManagers(); @@ -44,16 +55,19 @@ export class IntegrationManagers { this._client = MatrixClientPeg.get(); this._client.on("accountData", this._onAccountData.bind(this)); this._compileManagers(); + setInterval(() => this._setupHomeserverManagers(), HS_MANAGERS_REFRESH_INTERVAL); } stopWatching(): void { if (!this._client) return; this._client.removeListener("accountData", this._onAccountData.bind(this)); + if (this._wellknownRefreshTimerId !== null) clearInterval(this._wellknownRefreshTimerId); } _compileManagers() { this._managers = []; this._setupConfiguredManager(); + this._setupHomeserverManagers(); this._setupAccountManagers(); } @@ -63,6 +77,42 @@ export class IntegrationManagers { if (apiUrl && uiUrl) { this._managers.push(new IntegrationManagerInstance(KIND_CONFIG, apiUrl, uiUrl)); + this._primaryManager = null; // reset primary + } + } + + async _setupHomeserverManagers() { + try { + console.log("Updating homeserver-configured integration managers..."); + const homeserverDomain = MatrixClientPeg.getHomeserverName(); + const discoveryResponse = await AutoDiscovery.getRawClientConfig(homeserverDomain); + if (discoveryResponse && discoveryResponse['m.integrations']) { + let managers = discoveryResponse['m.integrations']['managers']; + if (!Array.isArray(managers)) managers = []; // make it an array so we can wipe the HS managers + + console.log(`Homeserver has ${managers.length} integration managers`); + + // Clear out any known managers for the homeserver + // TODO: Log out of the scalar clients + this._managers = this._managers.filter(m => m.kind !== KIND_HOMESERVER); + + // Now add all the managers the homeserver wants us to have + for (const hsManager of managers) { + if (!hsManager["api_url"]) continue; + this._managers.push(new IntegrationManagerInstance( + KIND_HOMESERVER, + hsManager["api_url"], + hsManager["ui_url"], // optional + )); + } + + this._primaryManager = null; // reset primary + } else { + console.log("Homeserver has no integration managers"); + } + } catch (e) { + console.error(e); + // Errors during discovery are non-fatal } } @@ -77,8 +127,11 @@ export class IntegrationManagers { const apiUrl = data['api_url']; if (!apiUrl || !uiUrl) return; - this._managers.push(new IntegrationManagerInstance(KIND_ACCOUNT, apiUrl, uiUrl)); + const manager = new IntegrationManagerInstance(KIND_ACCOUNT, apiUrl, uiUrl); + manager.id = w['id'] || w['state_key'] || ''; + this._managers.push(manager); }); + this._primaryManager = null; // reset primary } _onAccountData(ev: MatrixEvent): void { @@ -93,7 +146,20 @@ export class IntegrationManagers { getPrimaryManager(): IntegrationManagerInstance { if (this.hasManager()) { - return this._managers[this._managers.length - 1]; + if (this._primaryManager) return this._primaryManager; + + for (const kind of KIND_PREFERENCE) { + const managers = this._managers.filter(m => m.kind === kind); + if (!managers || !managers.length) continue; + + if (kind === KIND_ACCOUNT) { + // Order by state_keys (IDs) + managers.sort((a, b) => a.id.localeCompare(b.id)); + } + + this._primaryManager = managers[0]; + return this._primaryManager; + } } else { return null; } From 8493887cebdf71e21124ffd006ec0262c54f1b25 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Aug 2019 14:57:09 -0600 Subject: [PATCH 2/3] Import the right js-sdk --- src/integrations/IntegrationManagers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integrations/IntegrationManagers.js b/src/integrations/IntegrationManagers.js index 52486bd30e..f663698da9 100644 --- a/src/integrations/IntegrationManagers.js +++ b/src/integrations/IntegrationManagers.js @@ -21,7 +21,7 @@ import {IntegrationManagerInstance, KIND_ACCOUNT, KIND_CONFIG, KIND_HOMESERVER} import type {MatrixClient, MatrixEvent} from "matrix-js-sdk"; import WidgetUtils from "../utils/WidgetUtils"; import MatrixClientPeg from "../MatrixClientPeg"; -import {AutoDiscovery} from "../../../matrix-js-sdk"; +import {AutoDiscovery} from "matrix-js-sdk"; const HS_MANAGERS_REFRESH_INTERVAL = 8 * 60 * 60 * 1000; // 8 hours const KIND_PREFERENCE = [ From 470295ad14cbdf44638ba3f98201b39e160f28ab Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Aug 2019 15:17:59 -0600 Subject: [PATCH 3/3] Expose a getOrderedManagers() function for use elsewhere --- src/integrations/IntegrationManagers.js | 30 +++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/integrations/IntegrationManagers.js b/src/integrations/IntegrationManagers.js index f663698da9..98f605353c 100644 --- a/src/integrations/IntegrationManagers.js +++ b/src/integrations/IntegrationManagers.js @@ -144,22 +144,28 @@ export class IntegrationManagers { return this._managers.length > 0; } + getOrderedManagers(): IntegrationManagerInstance[] { + const ordered = []; + for (const kind of KIND_PREFERENCE) { + const managers = this._managers.filter(m => m.kind === kind); + if (!managers || !managers.length) continue; + + if (kind === KIND_ACCOUNT) { + // Order by state_keys (IDs) + managers.sort((a, b) => a.id.localeCompare(b.id)); + } + + ordered.push(...managers); + } + return ordered; + } + getPrimaryManager(): IntegrationManagerInstance { if (this.hasManager()) { if (this._primaryManager) return this._primaryManager; - for (const kind of KIND_PREFERENCE) { - const managers = this._managers.filter(m => m.kind === kind); - if (!managers || !managers.length) continue; - - if (kind === KIND_ACCOUNT) { - // Order by state_keys (IDs) - managers.sort((a, b) => a.id.localeCompare(b.id)); - } - - this._primaryManager = managers[0]; - return this._primaryManager; - } + this._primaryManager = this.getOrderedManagers()[0]; + return this._primaryManager; } else { return null; }