From 0f99d4fb81cb3c631c7ec9b1002fae9fe5d67fe3 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 11:58:03 +0100 Subject: [PATCH 1/5] Port Request to Typescript --- src/components/request.js | 103 ---------------------------------- src/components/request.ts | 113 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 103 deletions(-) delete mode 100644 src/components/request.js create mode 100644 src/components/request.ts diff --git a/src/components/request.js b/src/components/request.js deleted file mode 100644 index 8d7c36b3..00000000 --- a/src/components/request.js +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2020 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. -*/ - -var Promise = require("bluebird"); - -function generateRequestId() { - return (Math.random() * 1e20).toString(36); -} - -/** - * Construct a new Request. - * @constructor - * @param {Object} opts Options for this request. - * @param {string=} opts.id Optional ID to set on this request. One will be - * generated if this is not provided. - * @param {*=} opts.data Optional data to associate with this request. - */ -function Request(opts) { - opts = opts || {}; - this.id = opts.id || generateRequestId(); - this.data = opts.data; - this.startTs = Date.now(); - this.defer = new Promise.defer(); -} - -/** - * Get any optional data set on this request. - * @return {*} The data - */ -Request.prototype.getData = function() { - return this.data; -} - -/** - * Get this request's ID. - * @return {String} The ID. - */ -Request.prototype.getId = function() { - return this.id; -} - -/** - * Get the number of elapsed milliseconds since this request was created. - * @return {number} The number of milliseconds since this request was made. - */ -Request.prototype.getDuration = function() { - return Date.now() - this.startTs; -}; - -/** - * Retrieve a promise for this request which will be resolved/rejected when the - * respective methods are called on this Request. - * @return {Promise} A promise - */ -Request.prototype.getPromise = function() { - return this.defer.promise; -}; - -/** - * Resolve a request. This should be invoked for the successful processing - * of this request. This doesn't necessarily mean that the request was sent - * through, e.g. suppressing AS virtual users' messages is still a success. - * @param {*} msg The thing to resolve with. - */ -Request.prototype.resolve = function(msg) { - this.defer.resolve(msg); -}; - -/** - * Reject a request. This should be invoked for requests which failed to be - * processed correctly. - * @param {*} msg The thing to reject with. - */ -Request.prototype.reject = function(msg) { - this.defer.reject(msg); -}; - -/** - * Resolve or reject the promise depending on the outcome of this promise. - * @param {Promise} The promise whose resolution determines the outcome of this - * request. - */ -Request.prototype.outcomeFrom = function(promise) { - return promise.then( - (msg) => this.resolve(msg) - ).catch( - (msg) => this.reject(msg) - ); -} - -module.exports = Request; diff --git a/src/components/request.ts b/src/components/request.ts new file mode 100644 index 00000000..dc4570b3 --- /dev/null +++ b/src/components/request.ts @@ -0,0 +1,113 @@ +/* +Copyright 2020 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 Bluebird from "bluebird"; + +function generateRequestId() { + return (Math.random() * 1e20).toString(36); +} + +export interface RequestOpts { + id?: string; + data: T; +} + +export class Request { + private id: string; + private data: T; + private startTs: number; + private defer: Bluebird.Resolver; + + /** + * Construct a new Request. + * @param opts Options for this request. + * @param opts.id Optional ID to set on this request. One will be + * generated if this is not provided. + * @param opts.data Optional data to associate with this request. + */ + constructor(opts: RequestOpts) { + opts = opts || {}; + this.id = opts.id || generateRequestId(); + this.data = opts.data; + this.startTs = Date.now(); + this.defer = Bluebird.defer(); + } + + + /** + * Get any optional data set on this request. + * @return The data + */ + public getData() { + return this.data; + } + + /** + * Get this request's ID. + * @return The ID. + */ + public getId() { + return this.id; + } + + /** + * Get the number of elapsed milliseconds since this request was created. + * @return The number of milliseconds since this request was made. + */ + public getDuration() { + return Date.now() - this.startTs; + } + + /** + * Retrieve a promise for this request which will be resolved/rejected when the + * respective methods are called on this Request. + * @return {Promise} A promise + */ + public getPromise() { + return this.defer.promise; + } + + /** + * Resolve a request. This should be invoked for the successful processing + * of this request. This doesn't necessarily mean that the request was sent + * through, e.g. suppressing AS virtual users' messages is still a success. + * @param msg The thing to resolve with. + */ + public resolve(msg: unknown) { + this.defer.resolve(msg); + } + + /** + * Reject a request. This should be invoked for requests which failed to be + * processed correctly. + * @param msg The thing to reject with. + */ + public reject(msg: unknown) { + this.defer.reject(msg); + } + + /** + * Resolve or reject the promise depending on the outcome of this promise. + * @param promise The promise whose resolution determines the outcome of this + * request. + */ + public outcomeFrom(promise: Promise) { + return promise.then( + (msg) => this.resolve(msg) + ).catch( + (msg) => this.reject(msg) + ); + } +} From f3d51002452b58bf780959a1dc176cfc90595540 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 11:58:10 +0100 Subject: [PATCH 2/5] Port RequestFactory to Typescript --- src/components/request-factory.js | 91 ------------------------------ src/components/request-factory.ts | 92 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 91 deletions(-) delete mode 100644 src/components/request-factory.js create mode 100644 src/components/request-factory.ts diff --git a/src/components/request-factory.js b/src/components/request-factory.js deleted file mode 100644 index 04e3c70a..00000000 --- a/src/components/request-factory.js +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2020 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. -*/ - -var Request = require("./request"); - -/** - * Construct a factory which can create {@link Request} objects. Useful for - * adding "default" handlers to requests. - * @constructor - */ -function RequestFactory() { - this._resolves = []; - this._rejects = []; - this._timeouts = []; -} - -/** - * Generate a new request. - * @param {Object=} opts The options to pass to the Request constructor, if any. - * @return {Request} A new request object - */ -RequestFactory.prototype.newRequest = function(opts) { - var req = new Request(opts); - req.getPromise().then((res) => { - this._resolves.forEach((resolveFn) => { - resolveFn(req, res); - }); - }).catch((err) => { - this._rejects.forEach((rejectFn) => { - rejectFn(req, err); - }); - }); - - this._timeouts.forEach(function(timeoutObj) { - setTimeout(function() { - var promise = req.getPromise(); - if (!promise.isPending()) { - return; - } - timeoutObj.fn(req); - }, timeoutObj.timeout); - }); - return req; -} - -/** - * Add a function which will be invoked for every request that is resolved. - * @param {Function} fn The function to invoke. The first argument will be the - * Request object, the second will be the resolve argument. - */ -RequestFactory.prototype.addDefaultResolveCallback = function(fn) { - this._resolves.push(fn); -}; - -/** - * Add a function which will be invoked for every request that is rejected. - * @param {Function} fn The function to invoke. The first argument will be the - * Request object, the second will be the rejection argument. - */ -RequestFactory.prototype.addDefaultRejectCallback = function(fn) { - this._rejects.push(fn); -}; - -/** - * Add a function which will be invoked for every request that has not been - * resolved or rejected within a certain amount of time. - * @param {Function} fn The function to invoke. The first argument will be the - * Request object. - * @param {number} durationMs The number of milliseconds to wait for a - * resolution to the request. - */ -RequestFactory.prototype.addDefaultTimeoutCallback = function(fn, durationMs) { - this._timeouts.push({ - fn: fn, - timeout: durationMs - }); -}; - -module.exports = RequestFactory; diff --git a/src/components/request-factory.ts b/src/components/request-factory.ts new file mode 100644 index 00000000..40cdda33 --- /dev/null +++ b/src/components/request-factory.ts @@ -0,0 +1,92 @@ +/* +Copyright 2020 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 { Request, RequestOpts } from "./request"; + +type HandlerFunction = (req: Request, value: unknown) => Promise|unknown; +type TimeoutFunction = (req: Request) => void; + +/** + * A factory which can create {@link Request} objects. Useful for + * adding "default" handlers to requests. + */ +export class RequestFactory { + private _resolves: HandlerFunction[] = []; + private _rejects: HandlerFunction[] = []; + private _timeouts: {fn: TimeoutFunction, timeout: number}[] = []; + + + /** + * Generate a new request. + * @param opts The options to pass to the Request constructor, if any. + * @return A new request object + */ + public newRequest(opts: RequestOpts) { + const req = new Request(opts); + req.getPromise().then((res) => { + this._resolves.forEach((resolveFn) => { + resolveFn(req, res); + }); + }).catch((err) => { + this._rejects.forEach((rejectFn) => { + rejectFn(req, err); + }); + }); + + this._timeouts.forEach(function(timeoutObj) { + setTimeout(function() { + const promise = req.getPromise(); + if (!promise.isPending()) { + return; + } + timeoutObj.fn(req); + }, timeoutObj.timeout); + }); + return req; + } + + /** + * Add a function which will be invoked for every request that is resolved. + * @param fn The function to invoke. The first argument will be the + * Request object, the second will be the resolve argument. + */ + public addDefaultResolveCallback(fn: HandlerFunction) { + this._resolves.push(fn); + } + + /** + * Add a function which will be invoked for every request that is rejected. + * @param fn The function to invoke. The first argument will be the + * Request object, the second will be the rejection argument. + */ + public addDefaultRejectCallback(fn: HandlerFunction) { + this._rejects.push(fn); + } + + /** + * Add a function which will be invoked for every request that has not been + * resolved or rejected within a certain amount of time. + * @param fn The function to invoke. The first argument will be the + * Request object. + * @param durationMs The number of milliseconds to wait for a + * resolution to the request. + */ + public addDefaultTimeoutCallback(fn: TimeoutFunction, durationMs: number) { + this._timeouts.push({ + fn: fn, + timeout: durationMs + }); + } +} From 0716646e877d06e39baa5da397bdb9c000146f51 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 11:58:24 +0100 Subject: [PATCH 3/5] Update imports --- src/bridge.js | 2 +- src/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bridge.js b/src/bridge.js index 05882349..80b033b5 100644 --- a/src/bridge.js +++ b/src/bridge.js @@ -26,7 +26,7 @@ const MatrixScheduler = require("matrix-js-sdk").MatrixScheduler; const BridgeContext = require("./components/bridge-context"); const ClientFactory = require("./components/client-factory"); const AppServiceBot = require("./components/app-service-bot"); -const RequestFactory = require("./components/request-factory"); +const RequestFactory = require("./components/request-factory").RequestFactory; const Intent = require("./components/intent"); const RoomBridgeStore = require("./components/room-bridge-store"); const UserBridgeStore = require("./components/user-bridge-store"); diff --git a/src/index.ts b/src/index.ts index 2ab7897c..e2dd84b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,8 +23,8 @@ module.exports.Cli = require("./components/cli"); module.exports.ConfigValidator = require("./components/config-validator"); // Requests -module.exports.Request = require("./components/request"); -module.exports.RequestFactory = require("./components/request-factory"); +export * from "./components/request"; +export * from "./components/request-factory"; // Store module.exports.BridgeStore = require("./components/bridge-store"); From 1ad82b87edce47904c0c069d93958a34942a5158 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 12:00:56 +0100 Subject: [PATCH 4/5] changelog --- changelog.d/189.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/189.misc diff --git a/changelog.d/189.misc b/changelog.d/189.misc new file mode 100644 index 00000000..56583557 --- /dev/null +++ b/changelog.d/189.misc @@ -0,0 +1 @@ +Port RequestFactory and Request to Typescript \ No newline at end of file From fc8e9fe2bc1100f7bded299e097e1e3d6375a584 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 13:22:02 +0100 Subject: [PATCH 5/5] Shorten promise handler. Co-authored-by: Christian Paul --- src/components/request.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/request.ts b/src/components/request.ts index e96781c3..96380c1f 100644 --- a/src/components/request.ts +++ b/src/components/request.ts @@ -104,10 +104,6 @@ export class Request { * request. */ public outcomeFrom(promise: Promise) { - return promise.then( - (msg) => this.resolve(msg) - ).catch( - (msg) => this.reject(msg) - ); + return promise.then(this.resolve, this.reject); } }