mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-12-26 15:00:23 +05:30
80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
import { Options } from './request';
|
|
|
|
type Action = 'catch' | 'then' | 'before';
|
|
|
|
export interface MiddlewareRequestOptions {
|
|
url: string;
|
|
options: Options;
|
|
}
|
|
|
|
export interface Middleware {
|
|
before?: (options: MiddlewareRequestOptions) => Promise<MiddlewareRequestOptions>;
|
|
then?: (resp: any, options: MiddlewareRequestOptions) => Promise<any>;
|
|
catch?: (resp: any, options: MiddlewareRequestOptions, restart: () => Promise<any>) => Promise<any>;
|
|
}
|
|
|
|
/**
|
|
* A class to handle middleware layer
|
|
*/
|
|
class PromiseMiddlewareLayer {
|
|
private middlewares: Middleware[] = [];
|
|
|
|
/**
|
|
* Adds middleware into layer.
|
|
*
|
|
* `middleware` is an object, that may have multiple keys of type function.
|
|
* Each key is a name of an action that may be passed through the layer. Each
|
|
* function should return a Promise. An action with name catch has a special
|
|
* meaning: it will be invoked as catch callback on Promise (for middlewares,
|
|
* that should handle errors)
|
|
*
|
|
* @param {object} middleware
|
|
*/
|
|
add(middleware: Middleware) {
|
|
if (typeof middleware !== 'object') {
|
|
throw new Error('A middleware must be an object');
|
|
}
|
|
|
|
if (!this.middlewares.some((mdware) => mdware === middleware)) {
|
|
this.middlewares.push(middleware);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} action - the name of middleware's hook
|
|
* @param {object} data - the initial data to pass through middlewares chain
|
|
* @param {Function} restart - a function to restart current request (for `catch` hook)
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
run(
|
|
action: 'before',
|
|
options: MiddlewareRequestOptions,
|
|
arg3?: never,
|
|
arg4?: never,
|
|
): Promise<MiddlewareRequestOptions>;
|
|
run(action: 'then', resp: any, options: MiddlewareRequestOptions, arg4?: never): Promise<any>;
|
|
run(action: 'catch', resp: any, options: MiddlewareRequestOptions, restart: () => Promise<any>): Promise<any>;
|
|
run(action: Action, data: any, ...rest: any) {
|
|
const promiseMethod = action === 'catch' ? 'catch' : 'then';
|
|
|
|
return this.middlewares
|
|
.filter((middleware) => middleware[action])
|
|
.reduce(
|
|
(promise: Promise<any>, middleware) =>
|
|
invoke(promise, promiseMethod)((resp: any) => invoke(middleware, action)(resp, ...rest)),
|
|
invoke(Promise, action === 'catch' ? 'reject' : 'resolve')(data),
|
|
);
|
|
}
|
|
}
|
|
|
|
function invoke(instance: Record<string, any>, method: string) {
|
|
if (typeof instance[method] !== 'function') {
|
|
throw new Error(`Can not invoke ${method} on ${instance}`);
|
|
}
|
|
|
|
return (...args: any) => instance[method](...args);
|
|
}
|
|
|
|
export default PromiseMiddlewareLayer;
|