From 7403cbc3894539b3cb134f4370b684ed3077a97e Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Tue, 23 Nov 2021 18:09:33 +0530 Subject: [PATCH] WIP - HomeServerApi.js to ts conversion --- src/matrix/SessionContainer.js | 2 +- .../{HomeServerApi.js => HomeServerApi.ts} | 149 ++++++++++-------- src/matrix/net/HomeServerRequest.ts | 4 +- src/matrix/net/Reconnector.ts | 6 +- src/matrix/net/RequestScheduler.ts | 2 +- src/matrix/net/common.ts | 2 +- src/platform/types/Platform.ts | 13 +- 7 files changed, 99 insertions(+), 79 deletions(-) rename src/matrix/net/{HomeServerApi.js => HomeServerApi.ts} (55%) diff --git a/src/matrix/SessionContainer.js b/src/matrix/SessionContainer.js index 7bbe74cb..677cde34 100644 --- a/src/matrix/SessionContainer.js +++ b/src/matrix/SessionContainer.js @@ -19,7 +19,7 @@ import {createEnum} from "../utils/enum"; import {lookupHomeserver} from "./well-known.js"; import {AbortableOperation} from "../utils/AbortableOperation"; import {ObservableValue} from "../observable/ObservableValue"; -import {HomeServerApi} from "./net/HomeServerApi.js"; +import {HomeServerApi} from "./net/HomeServerApi"; import {Reconnector, ConnectionStatus} from "./net/Reconnector"; import {ExponentialRetryDelay} from "./net/ExponentialRetryDelay"; import {MediaRepository} from "./net/MediaRepository"; diff --git a/src/matrix/net/HomeServerApi.js b/src/matrix/net/HomeServerApi.ts similarity index 55% rename from src/matrix/net/HomeServerApi.js rename to src/matrix/net/HomeServerApi.ts index fe5e470b..6cf1153c 100644 --- a/src/matrix/net/HomeServerApi.js +++ b/src/matrix/net/HomeServerApi.ts @@ -17,12 +17,23 @@ limitations under the License. import {encodeQueryParams, encodeBody} from "./common"; import {HomeServerRequest} from "./HomeServerRequest"; +import type {Reconnector} from "./Reconnector"; +import type {IEncodedBody} from "./common"; +import type {IRequestOptions, RequestFunction} from "../../platform/types/Platform"; +import type {LogItem} from "../../logging/LogItem"; + +type RequestMethod = "POST" | "GET" | "PUT"; const CS_R0_PREFIX = "/_matrix/client/r0"; const DEHYDRATION_PREFIX = "/_matrix/client/unstable/org.matrix.msc2697.v2"; export class HomeServerApi { - constructor({homeserver, accessToken, request, reconnector}) { + private readonly _homeserver: string; + private readonly _accessToken: string; + private readonly _requestFn: RequestFunction; + private readonly _reconnector: Reconnector; + + constructor({homeserver, accessToken, request, reconnector}: {homeserver: string, accessToken: string, request: RequestFunction, reconnector: Reconnector}) { // store these both in a closure somehow so it's harder to get at in case of XSS? // one could change the homeserver as well so the token gets sent there, so both must be protected from read/write this._homeserver = homeserver; @@ -31,14 +42,14 @@ export class HomeServerApi { this._reconnector = reconnector; } - _url(csPath, prefix = CS_R0_PREFIX) { + _url(csPath: string, prefix: string = CS_R0_PREFIX): string { return this._homeserver + prefix + csPath; } - _baseRequest(method, url, queryParams, body, options, accessToken) { + _baseRequest(method: RequestMethod, url: string, queryParams?: object, body?: object, options?: IRequestOptions, accessToken?: string): HomeServerRequest { const queryString = encodeQueryParams(queryParams); url = `${url}?${queryString}`; - let log; + let log: LogItem | undefined; if (options?.log) { const parent = options?.log; log = parent.child({ @@ -47,8 +58,8 @@ export class HomeServerApi { method, }, parent.level.Info); } - let encodedBody; - const headers = new Map(); + let encodedBody: IEncodedBody["body"]; + const headers: Map = new Map(); if (accessToken) { headers.set("Authorization", `Bearer ${accessToken}`); } @@ -56,6 +67,7 @@ export class HomeServerApi { if (body) { const encoded = encodeBody(body); headers.set("Content-Type", encoded.mimeType); + //todo: remove this? headers.set("Content-Length", encoded.length); encodedBody = encoded.body; } @@ -86,63 +98,63 @@ export class HomeServerApi { return hsRequest; } - _unauthedRequest(method, url, queryParams, body, options) { - return this._baseRequest(method, url, queryParams, body, options, null); + _unauthedRequest(method: RequestMethod, url: string, queryParams?: object, body?: object, options?: IRequestOptions): HomeServerRequest { + return this._baseRequest(method, url, queryParams, body, options); } - _authedRequest(method, url, queryParams, body, options) { + _authedRequest(method: RequestMethod, url: string, queryParams?: object, body?: object, options?: IRequestOptions): HomeServerRequest { return this._baseRequest(method, url, queryParams, body, options, this._accessToken); } - _post(csPath, queryParams, body, options) { + _post(csPath: string, queryParams: object, body: object, options?: IRequestOptions): HomeServerRequest { return this._authedRequest("POST", this._url(csPath, options?.prefix || CS_R0_PREFIX), queryParams, body, options); } - _put(csPath, queryParams, body, options) { + _put(csPath: string, queryParams: object, body?: object, options?: IRequestOptions): HomeServerRequest { return this._authedRequest("PUT", this._url(csPath, options?.prefix || CS_R0_PREFIX), queryParams, body, options); } - _get(csPath, queryParams, body, options) { + _get(csPath: string, queryParams?: object, body?: object, options?: IRequestOptions): HomeServerRequest { return this._authedRequest("GET", this._url(csPath, options?.prefix || CS_R0_PREFIX), queryParams, body, options); } - sync(since, filter, timeout, options = null) { - return this._get("/sync", {since, timeout, filter}, null, options); + sync(since: string, filter: string, timeout: number, options?: IRequestOptions) { + return this._get("/sync", {since, timeout, filter}, undefined, options); } // params is from, dir and optionally to, limit, filter. - messages(roomId, params, options = null) { - return this._get(`/rooms/${encodeURIComponent(roomId)}/messages`, params, null, options); + messages(roomId: string, params: object, options?: IRequestOptions) { + return this._get(`/rooms/${encodeURIComponent(roomId)}/messages`, params, undefined, options); } // params is at, membership and not_membership - members(roomId, params, options = null) { - return this._get(`/rooms/${encodeURIComponent(roomId)}/members`, params, null, options); + members(roomId: string, params: object, options?: IRequestOptions) { + return this._get(`/rooms/${encodeURIComponent(roomId)}/members`, params, undefined, options); } - send(roomId, eventType, txnId, content, options = null) { + send(roomId: string, eventType: string, txnId: string, content: object, options?: IRequestOptions) { return this._put(`/rooms/${encodeURIComponent(roomId)}/send/${encodeURIComponent(eventType)}/${encodeURIComponent(txnId)}`, {}, content, options); } - redact(roomId, eventId, txnId, content, options = null) { + redact(roomId: string, eventId: string, txnId: string, content: object, options?: IRequestOptions) { return this._put(`/rooms/${encodeURIComponent(roomId)}/redact/${encodeURIComponent(eventId)}/${encodeURIComponent(txnId)}`, {}, content, options); } - receipt(roomId, receiptType, eventId, options = null) { + receipt(roomId: string, receiptType: string, eventId: string, options?: IRequestOptions) { return this._post(`/rooms/${encodeURIComponent(roomId)}/receipt/${encodeURIComponent(receiptType)}/${encodeURIComponent(eventId)}`, {}, {}, options); } - state(roomId, eventType, stateKey, options = null) { - return this._get(`/rooms/${encodeURIComponent(roomId)}/state/${encodeURIComponent(eventType)}/${encodeURIComponent(stateKey)}`, {}, null, options); + state(roomId: string, eventType: string, stateKey: string, options?: IRequestOptions) { + return this._get(`/rooms/${encodeURIComponent(roomId)}/state/${encodeURIComponent(eventType)}/${encodeURIComponent(stateKey)}`, {}, undefined, options); } getLoginFlows() { - return this._unauthedRequest("GET", this._url("/login"), null, null, null); + return this._unauthedRequest("GET", this._url("/login")); } - passwordLogin(username, password, initialDeviceDisplayName, options = null) { - return this._unauthedRequest("POST", this._url("/login"), null, { + passwordLogin(username: string, password: string, initialDeviceDisplayName: string, options?: IRequestOptions) { + return this._unauthedRequest("POST", this._url("/login"), undefined, { "type": "m.login.password", "identifier": { "type": "m.id.user", @@ -153,8 +165,8 @@ export class HomeServerApi { }, options); } - tokenLogin(loginToken, txnId, initialDeviceDisplayName, options = null) { - return this._unauthedRequest("POST", this._url("/login"), null, { + tokenLogin(loginToken: string, txnId: string, initialDeviceDisplayName: string, options?: IRequestOptions) { + return this._unauthedRequest("POST", this._url("/login"), undefined, { "type": "m.login.token", "identifier": { "type": "m.id.user", @@ -165,91 +177,91 @@ export class HomeServerApi { }, options); } - createFilter(userId, filter, options = null) { - return this._post(`/user/${encodeURIComponent(userId)}/filter`, null, filter, options); + createFilter(userId: string, filter: object, options?: IRequestOptions) { + return this._post(`/user/${encodeURIComponent(userId)}/filter`, {}, filter, options); } - versions(options = null) { - return this._unauthedRequest("GET", `${this._homeserver}/_matrix/client/versions`, null, null, options); + versions(options?: IRequestOptions) { + return this._unauthedRequest("GET", `${this._homeserver}/_matrix/client/versions`, undefined, undefined, options); } - uploadKeys(dehydratedDeviceId, payload, options = null) { + uploadKeys(dehydratedDeviceId: string, payload: object, options?: IRequestOptions) { let path = "/keys/upload"; if (dehydratedDeviceId) { path = path + `/${encodeURIComponent(dehydratedDeviceId)}`; } - return this._post(path, null, payload, options); + return this._post(path, {}, payload, options); } - queryKeys(queryRequest, options = null) { - return this._post("/keys/query", null, queryRequest, options); + queryKeys(queryRequest: object, options?: IRequestOptions) { + return this._post("/keys/query", {}, queryRequest, options); } - claimKeys(payload, options = null) { - return this._post("/keys/claim", null, payload, options); + claimKeys(payload: object, options?: IRequestOptions) { + return this._post("/keys/claim", {}, payload, options); } - sendToDevice(type, payload, txnId, options = null) { - return this._put(`/sendToDevice/${encodeURIComponent(type)}/${encodeURIComponent(txnId)}`, null, payload, options); + sendToDevice(type: string, payload: object, txnId: string, options?: IRequestOptions) { + return this._put(`/sendToDevice/${encodeURIComponent(type)}/${encodeURIComponent(txnId)}`, {}, payload, options); } - roomKeysVersion(version = null, options = null) { + roomKeysVersion(version?: string, options?: IRequestOptions) { let versionPart = ""; if (version) { versionPart = `/${encodeURIComponent(version)}`; } - return this._get(`/room_keys/version${versionPart}`, null, null, options); + return this._get(`/room_keys/version${versionPart}`, undefined, undefined, options); } - roomKeyForRoomAndSession(version, roomId, sessionId, options = null) { - return this._get(`/room_keys/keys/${encodeURIComponent(roomId)}/${encodeURIComponent(sessionId)}`, {version}, null, options); + roomKeyForRoomAndSession(version: string, roomId: string, sessionId: string, options?: IRequestOptions) { + return this._get(`/room_keys/keys/${encodeURIComponent(roomId)}/${encodeURIComponent(sessionId)}`, {version}, undefined, options); } - uploadAttachment(blob, filename, options = null) { + uploadAttachment(blob: Blob, filename: string, options?: IRequestOptions) { return this._authedRequest("POST", `${this._homeserver}/_matrix/media/r0/upload`, {filename}, blob, options); } - setPusher(pusher, options = null) { - return this._post("/pushers/set", null, pusher, options); + setPusher(pusher: object, options?: IRequestOptions) { + return this._post("/pushers/set", {}, pusher, options); } - getPushers(options = null) { - return this._get("/pushers", null, null, options); + getPushers(options?: IRequestOptions) { + return this._get("/pushers", undefined, undefined, options); } - join(roomId, options = null) { - return this._post(`/rooms/${encodeURIComponent(roomId)}/join`, null, null, options); + join(roomId: string, options?: IRequestOptions) { + return this._post(`/rooms/${encodeURIComponent(roomId)}/join`, {}, {}, options); } - joinIdOrAlias(roomIdOrAlias, options = null) { - return this._post(`/join/${encodeURIComponent(roomIdOrAlias)}`, null, null, options); + joinIdOrAlias(roomIdOrAlias: string, options?: IRequestOptions) { + return this._post(`/join/${encodeURIComponent(roomIdOrAlias)}`, {}, {}, options); } - leave(roomId, options = null) { - return this._post(`/rooms/${encodeURIComponent(roomId)}/leave`, null, null, options); + leave(roomId: string, options?: IRequestOptions) { + return this._post(`/rooms/${encodeURIComponent(roomId)}/leave`, {}, {}, options); } - forget(roomId, options = null) { - return this._post(`/rooms/${encodeURIComponent(roomId)}/forget`, null, null, options); + forget(roomId: string, options?: IRequestOptions) { + return this._post(`/rooms/${encodeURIComponent(roomId)}/forget`, {}, {}, options); } - logout(options = null) { - return this._post(`/logout`, null, null, options); + logout(options?: IRequestOptions) { + return this._post(`/logout`, {}, {}, options); } - getDehydratedDevice(options = {}) { + getDehydratedDevice(options: IRequestOptions) { options.prefix = DEHYDRATION_PREFIX; - return this._get(`/dehydrated_device`, null, null, options); + return this._get(`/dehydrated_device`, undefined, undefined, options); } - createDehydratedDevice(payload, options = {}) { + createDehydratedDevice(payload: object, options: IRequestOptions) { options.prefix = DEHYDRATION_PREFIX; - return this._put(`/dehydrated_device`, null, payload, options); + return this._put(`/dehydrated_device`, {}, payload, options); } - claimDehydratedDevice(deviceId, options = {}) { + claimDehydratedDevice(deviceId: string, options: IRequestOptions) { options.prefix = DEHYDRATION_PREFIX; - return this._post(`/dehydrated_device/claim`, null, {device_id: deviceId}, options); + return this._post(`/dehydrated_device/claim`, {}, {device_id: deviceId}, options); } } @@ -258,11 +270,14 @@ import {Request as MockRequest} from "../../mocks/Request.js"; export function tests() { return { "superficial happy path for GET": async assert => { + // @ts-ignore const hsApi = new HomeServerApi({ request: () => new MockRequest().respond(200, 42), - homeserver: "https://hs.tld" + homeserver: "https://hs.tld", }); - const result = await hsApi._get("foo", null, null, null).response(); + const result = await hsApi + ._get("foo", undefined, undefined, undefined) + .response(); assert.strictEqual(result, 42); } } diff --git a/src/matrix/net/HomeServerRequest.ts b/src/matrix/net/HomeServerRequest.ts index 7f323181..aad45f1b 100644 --- a/src/matrix/net/HomeServerRequest.ts +++ b/src/matrix/net/HomeServerRequest.ts @@ -23,7 +23,7 @@ export class HomeServerRequest { // todo: Shouldn't log be of type ILogItem; but ILogItem does not have finish method private readonly _log?: LogItem; private _sourceRequest?: RequestResult; - private readonly _promise: Promise; + private readonly _promise: Promise; constructor(method: string, url: string, sourceRequest: RequestResult, log?: LogItem) { this._log = log; @@ -96,7 +96,7 @@ export class HomeServerRequest { } } - response(): Promise { + response(): Promise { return this._promise; } } diff --git a/src/matrix/net/Reconnector.ts b/src/matrix/net/Reconnector.ts index 7f9c9660..b1394425 100644 --- a/src/matrix/net/Reconnector.ts +++ b/src/matrix/net/Reconnector.ts @@ -19,7 +19,7 @@ import type {ExponentialRetryDelay} from "./ExponentialRetryDelay"; import type {TimeMeasure} from "../../platform/web/dom/Clock.js"; import type {OnlineStatus} from "../../platform/web/dom/OnlineStatus.js"; import type {IVersionResponse} from "./types/response"; -import type {HomeServerApi} from "./HomeServerApi.js"; +import type {HomeServerApi} from "./HomeServerApi"; export enum ConnectionStatus { "Waiting", @@ -33,7 +33,7 @@ export class Reconnector { private readonly _onlineStatus: OnlineStatus; private readonly _state: ObservableValue; private _isReconnecting: boolean; - private _versionsResponse?: IVersionResponse = undefined; + private _versionsResponse?: IVersionResponse; private _stateSince: TimeMeasure; constructor({retryDelay, createMeasure, onlineStatus}: {retryDelay: ExponentialRetryDelay, createMeasure: () => TimeMeasure, onlineStatus: OnlineStatus}) { @@ -164,6 +164,7 @@ export function tests() { const subscription = reconnector.connectionStatus.subscribe(s => { statuses.push(s); }); + // @ts-ignore reconnector.onRequestFailed(createHsApiMock(1)); await connectionStatus.waitFor(s => s === ConnectionStatus.Waiting).promise; clock.elapse(2000); @@ -184,6 +185,7 @@ export function tests() { const retryDelay = new _ExponentialRetryDelay(clock.createTimeout); const reconnector = new Reconnector({retryDelay, onlineStatus, createMeasure}); const {connectionStatus} = reconnector; + // @ts-ignore reconnector.onRequestFailed(createHsApiMock(1)); await connectionStatus.waitFor(s => s === ConnectionStatus.Waiting).promise; onlineStatus.set(true); //skip waiting diff --git a/src/matrix/net/RequestScheduler.ts b/src/matrix/net/RequestScheduler.ts index c11a81d0..b47845c4 100644 --- a/src/matrix/net/RequestScheduler.ts +++ b/src/matrix/net/RequestScheduler.ts @@ -17,7 +17,7 @@ limitations under the License. import {AbortError} from "../../utils/error"; import {HomeServerError} from "../error.js"; -import {HomeServerApi} from "./HomeServerApi.js"; +import {HomeServerApi} from "./HomeServerApi"; import {ExponentialRetryDelay} from "./ExponentialRetryDelay"; import {Clock} from "../../platform/web/dom/Clock.js"; import type {HomeServerRequest} from "./HomeServerRequest.js"; diff --git a/src/matrix/net/common.ts b/src/matrix/net/common.ts index ce9baeba..7a07c445 100644 --- a/src/matrix/net/common.ts +++ b/src/matrix/net/common.ts @@ -23,7 +23,7 @@ export interface IEncodedBody { length: number; } -export function encodeQueryParams(queryParams: object): string { +export function encodeQueryParams(queryParams?: object): string { return Object.entries(queryParams || {}) .filter(([, value]) => value !== undefined) .map(([name, value]) => { diff --git a/src/platform/types/Platform.ts b/src/platform/types/Platform.ts index b8b715ed..8390a92d 100644 --- a/src/platform/types/Platform.ts +++ b/src/platform/types/Platform.ts @@ -18,15 +18,18 @@ limitations under the License. import type {RequestResult} from "../web/dom/request/fetch.js"; import type {IEncodedBody} from "../../matrix/net/common"; +import {LogItem} from "../../logging/LogItem"; -interface IRequestOptions { +export interface IRequestOptions { uploadProgress?: (loadedBytes: number) => void; timeout?: number; body?: IEncodedBody; - headers?: { [key: string]: number | string }; + headers?: Map; cache?: boolean; - method: string; - format: string; + log?: LogItem; + prefix?: string; + method?: string; + format?: string; } -export type Request = (url: string, options: IRequestOptions) => RequestResult; +export type RequestFunction = (url: string, options: IRequestOptions) => RequestResult;