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)
This commit is contained in:
Travis Ralston 2019-08-22 14:49:20 -06:00
parent efe8254985
commit a35735da45
2 changed files with 71 additions and 3 deletions

View file

@ -23,11 +23,13 @@ import url from 'url';
export const KIND_ACCOUNT = "account"; export const KIND_ACCOUNT = "account";
export const KIND_CONFIG = "config"; export const KIND_CONFIG = "config";
export const KIND_HOMESERVER = "homeserver";
export class IntegrationManagerInstance { export class IntegrationManagerInstance {
apiUrl: string; apiUrl: string;
uiUrl: string; uiUrl: string;
kind: string; kind: string;
id: string; // only applicable in some cases
constructor(kind: string, apiUrl: string, uiUrl: string) { constructor(kind: string, apiUrl: string, uiUrl: string) {
this.kind = kind; this.kind = kind;

View file

@ -17,10 +17,19 @@ limitations under the License.
import SdkConfig from '../SdkConfig'; import SdkConfig from '../SdkConfig';
import sdk from "../index"; import sdk from "../index";
import Modal from '../Modal'; 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 type {MatrixClient, MatrixEvent} from "matrix-js-sdk";
import WidgetUtils from "../utils/WidgetUtils"; import WidgetUtils from "../utils/WidgetUtils";
import MatrixClientPeg from "../MatrixClientPeg"; 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 { export class IntegrationManagers {
static _instance; static _instance;
@ -34,6 +43,8 @@ export class IntegrationManagers {
_managers: IntegrationManagerInstance[] = []; _managers: IntegrationManagerInstance[] = [];
_client: MatrixClient; _client: MatrixClient;
_wellknownRefreshTimerId: number = null;
_primaryManager: IntegrationManagerInstance;
constructor() { constructor() {
this._compileManagers(); this._compileManagers();
@ -44,16 +55,19 @@ export class IntegrationManagers {
this._client = MatrixClientPeg.get(); this._client = MatrixClientPeg.get();
this._client.on("accountData", this._onAccountData.bind(this)); this._client.on("accountData", this._onAccountData.bind(this));
this._compileManagers(); this._compileManagers();
setInterval(() => this._setupHomeserverManagers(), HS_MANAGERS_REFRESH_INTERVAL);
} }
stopWatching(): void { stopWatching(): void {
if (!this._client) return; if (!this._client) return;
this._client.removeListener("accountData", this._onAccountData.bind(this)); this._client.removeListener("accountData", this._onAccountData.bind(this));
if (this._wellknownRefreshTimerId !== null) clearInterval(this._wellknownRefreshTimerId);
} }
_compileManagers() { _compileManagers() {
this._managers = []; this._managers = [];
this._setupConfiguredManager(); this._setupConfiguredManager();
this._setupHomeserverManagers();
this._setupAccountManagers(); this._setupAccountManagers();
} }
@ -63,6 +77,42 @@ export class IntegrationManagers {
if (apiUrl && uiUrl) { if (apiUrl && uiUrl) {
this._managers.push(new IntegrationManagerInstance(KIND_CONFIG, 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']; const apiUrl = data['api_url'];
if (!apiUrl || !uiUrl) return; 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 { _onAccountData(ev: MatrixEvent): void {
@ -93,7 +146,20 @@ export class IntegrationManagers {
getPrimaryManager(): IntegrationManagerInstance { getPrimaryManager(): IntegrationManagerInstance {
if (this.hasManager()) { 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 { } else {
return null; return null;
} }