UNPKG

miter

Version:

A typescript web framework based on ExpressJs based loosely on SailsJs

380 lines 19.4 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); require("reflect-metadata"); const injector_1 = require("../core/injector"); const injectable_decorator_1 = require("../decorators/services/injectable.decorator"); const name_decorator_1 = require("../decorators/services/name.decorator"); const controller_1 = require("../metadata/router/controller"); const route_1 = require("../metadata/router/route"); const router_1 = require("../metadata/server/router"); const policy_1 = require("../metadata/policies/policy"); const logger_1 = require("../services/logger"); const error_handler_1 = require("../services/error-handler"); const router_service_1 = require("../services/router.service"); const inheritance_hierarchy_1 = require("../util/inheritance-hierarchy"); const join_route_paths_1 = require("../util/join-route-paths"); const wrap_promise_1 = require("../util/wrap-promise"); const http_status_type_1 = require("../util/http-status-type"); require("./extend-req-res"); let RouterReflector = RouterReflector_1 = class RouterReflector { constructor(injector, logger, errorHandler, routerMeta, _router) { this.injector = injector; this.logger = logger; this.errorHandler = errorHandler; this.routerMeta = routerMeta; this._router = _router; this.controllersReflected = 0; this.routesReflected = 0; this.controllers = {}; this._interceptors = []; this.unfinishedRoutes = 0; this.requestIndex = 0; this.registerRouteInterceptor((req, res, next) => __awaiter(this, void 0, void 0, function* () { try { yield next(); } catch (e) { this.logger.error(e); if (!res.errorResult) throw e; } })); } get router() { return this._router; } reflectServerRoutes(app) { this.logger.verbose(`Loading routes...`); this.reflectRoutes(this.routerMeta.controllers); app.use(this.router.expressRouter); this.logger.info(`${this.controllersReflected} controllers reflected.`); this.logger.info(`${this.routesReflected} routes reflected.`); this.logger.info(`Finished loading routes.`); } reflectRoutes(controllers, parentControllers) { parentControllers = parentControllers || []; this.logger.verbose(`in reflectRoutes; controllers=[${controllers && controllers.map(c => c.name || c)}]; parentControllers=[${parentControllers && parentControllers.map(c => c.name || c)}]`); for (let q = 0; q < controllers.length; q++) { this.reflectControllerRoutes(parentControllers, controllers[q]); } } reflectControllerRoutes(parentControllers, controllerFn) { if (this.controllers[controllerFn]) throw new Error(`A controller was passed to the router-reflector twice: ${controllerFn.name || controllerFn}.`); let controllerInst = this.controllers[controllerFn] = this.injector.resolveInjectable(controllerFn); let controllerProto = controllerFn.prototype; let meta = Reflect.getOwnMetadata(controller_1.ControllerMetadataSym, controllerProto); if (!meta) throw new Error(`Expecting class with @Controller decorator, could not reflect routes for ${controllerFn.name || controllerFn}.`); this.logger.verbose(`Reflecting routes for controller ${controllerFn.name || controllerFn}`); this.controllersReflected++; parentControllers = parentControllers || []; let parentMeta = parentControllers.map(pc => Reflect.getOwnMetadata(controller_1.ControllerMetadataSym, pc.prototype)); if (parentMeta.some(pm => !pm)) throw new Error(`Failed to reflect parent controller metadata for controller: ${controllerFn.name || controllerFn}`); let routes = this.reflectRouteMeta(controllerProto); for (let q = 0; q < routes.length; q++) { let [routeFnName, routeMetaArr] = routes[q]; for (let w = 0; w < routeMetaArr.length; w++) { let routeMeta = routeMetaArr[w]; this.addRoute(parentMeta, controllerInst, routeFnName, meta, routeMeta); } } this.reflectRoutes(meta.controllers || [], [...parentControllers, controllerFn]); } registerRouteInterceptor(interceptor) { this._interceptors.push(interceptor); } reflectRouteMeta(controllerProto) { let hierarchy = inheritance_hierarchy_1.inhertitanceHierarchy(controllerProto); this.logger.verbose('reflecting routes for inheritance hierarchy:', hierarchy.map(fn => fn.name || fn)); let routeMeta = []; for (let r = 0; r < hierarchy.length; r++) { let fn = hierarchy[r]; let routeNames = Reflect.getOwnMetadata(controller_1.ControllerRoutesSym, fn) || []; for (let q = 0; q < routeNames.length; q++) { let routeFnName = routeNames[q]; let routeMetaArr = Reflect.getOwnMetadata(route_1.RouteMetadataSym, fn, routeFnName) || []; routeMeta.push([routeFnName, routeMetaArr]); } } return routeMeta; } addRoute(parentMeta, controller, routeFnName, controllerMeta, routeMeta) { let controllerName = this.getControllerName(controller); let routeMethodName = `${controllerName}#${routeFnName}`; if (typeof routeMeta.method === 'undefined') throw new Error(`Failed to create route ${controller}.${routeFnName}. No method set!`); let pathPart = routeMeta.path; if (typeof controller.transformRoutePathPart === 'function') { pathPart = controller.transformRoutePathPart(routeFnName, pathPart) || pathPart; } let fullPath = join_route_paths_1.joinRoutePaths(...[ this.routerMeta.path, ...parentMeta.map(pm => pm.path || ''), controllerMeta.path || '', pathPart ]); if (controller.transformRoutePath) { fullPath = controller.transformRoutePath(routeFnName, fullPath) || fullPath; } let policyDescriptors = [ ...(this.routerMeta.policies), ...this.getParentPolicyDescriptors(parentMeta), ...(controllerMeta.policies || []), ...(routeMeta.policies || []) ]; if (typeof controller.transformRoutePolicies === 'function') { policyDescriptors = controller.transformRoutePolicies(routeFnName, fullPath, policyDescriptors) || policyDescriptors; } if (typeof controller.transformRoute === 'function') { let route = { routeFnName, fullPath, policyDescriptors }; let result = controller.transformRoute(route); [routeFnName, fullPath, policyDescriptors] = [route.routeFnName, route.fullPath, route.policyDescriptors]; if (typeof result === 'boolean' && !result) { this.logger.verbose(`... Skipping route ${routeFnName} (${routeMeta.method.toUpperCase()} ${fullPath})`); return; } } policyDescriptors = this.flattenPolicies(policyDescriptors); let policies = this.resolvePolicies(policyDescriptors); if (!controller[routeFnName]) throw new Error(`There is no route handler for ${controllerName}.${routeFnName}`); if (typeof controller[routeFnName] !== 'function') throw new Error(`The route handler for ${controllerName}.${routeFnName} is not a function`); let boundRoute = controller[routeFnName].bind(controller); this.logger.verbose(`& Adding route ${routeFnName} (${routeMeta.method.toUpperCase()} ${fullPath})`); this.routesReflected++; let addRouteFn = this.router.expressRouter[routeMeta.method].bind(this.router.expressRouter); let fullRouterFn = this.createFullRouterFn(policies, boundRoute, routeMethodName); addRouteFn(fullPath, fullRouterFn); } getControllerName(controller) { if (!controller) { throw new Error(`Cannot extract name from falsey controller: ${controller}`); } else if (controller.constructor && controller.constructor.name) { return controller.constructor.name; } else if (controller.name) { return controller.name; } else return controller; } getParentPolicyDescriptors(parentMeta) { let policies = []; for (let pm of parentMeta) { policies.push(...(pm.policies || [])); } return policies; } flattenPolicies(descriptors) { let aggregate = []; this.flattenPolicies_recursive(descriptors, aggregate); return aggregate; } flattenPolicies_recursive(descriptors, aggregate) { for (let policy of descriptors) { if (this.isPolicyCtor(policy)) { let policyMeta = Reflect.getOwnMetadata(policy_1.PolicyMetadataSym, policy.prototype) || {}; let nestedPolicies = policyMeta.policies; if (nestedPolicies) this.flattenPolicies_recursive(nestedPolicies, aggregate); } let found = false; for (let q = 0; q < aggregate.length; q++) { if (aggregate[q] === policy) { found = true; break; } } if (!found) aggregate.push(policy); } } resolvePolicies(descriptors) { return descriptors.map((desc) => { let key; let fn; if (this.isPolicyCtor(desc)) { key = desc; let val = this.injector.resolveInjectable(desc); if (!val) throw new Error(`Could not resolve dependency for policy: ${desc.name || desc}`); desc = val; } if (this.isPolicyT(desc)) { fn = desc.handle.bind(desc); } else { let handler = desc; fn = function (req, res) { return __awaiter(this, void 0, void 0, function* () { return yield wrap_promise_1.wrapPromise(handler, req, res); }); }; } return [key, fn]; }); } isPolicyCtor(desc) { if (this.isPolicyT(desc)) return false; let ctorFn = desc; return !!(ctorFn.prototype && ctorFn.prototype.handle); } isPolicyT(desc) { return !!desc.handle; } createFullRouterFn(policies, boundRoute, routeMethodName) { let fullRouterFn = function (requestIndex, req, res) { return __awaiter(this, void 0, void 0, function* () { this.logger.info(`{${requestIndex}} beginning request: ${req.url}`); this.logger.verbose(`{${requestIndex}} unfinishedRoutes: ${++this.unfinishedRoutes}`); let allResults = []; req.policyResults = this.createPolicyResultsFn(policies, allResults); let initialStatusCode = res.statusCode; for (let q = 0; q < policies.length; q++) { let policy = policies[q]; let result; let policyCtor = policy[0]; let policyName = (policyCtor && (policyCtor.name || policyCtor)) || '(undefined)'; try { this.logger.verbose(`{${requestIndex}} awaiting policy ${q + 1}/${policies.length} (${policyName})`); result = yield policy[1](req, res); this.logger.verbose(`{${requestIndex}} policy ${policyName} returned with result ${JSON.stringify(result)}`); } catch (e) { this.logger.error(`{${requestIndex}} policy (${policyName}) threw an exception.`); this.logger.error(e); let errorResult = this.errorHandler.handleRouteError(e, req, res); if (typeof errorResult !== 'boolean' && typeof errorResult !== 'undefined' && errorResult !== null) errorResult = yield errorResult; if (res.statusCode === initialStatusCode) { this.logger.error(`Error handler did not send a response. Serving 500 - Internal server error`); res.status(http_status_type_1.HTTP_STATUS_INTERNAL_SERVER_ERROR); res.send('Internal server error'); this.logger.verbose(`{${requestIndex}} ending request. unfinishedRoutes: ${--this.unfinishedRoutes}`); } if (!errorResult) throw e; return; } allResults.push(result); if (res.statusCode !== initialStatusCode || res.headersSent) return; } this.logger.verbose(`{${requestIndex}} policies complete`); let failed = false; try { this.logger.verbose(`{${requestIndex}} calling route`); yield boundRoute(req, res); this.logger.verbose(`{${requestIndex}} route complete`); } catch (e) { this.logger.error(`{${requestIndex}} route threw an exception. unfinishedRoutes: ${this.unfinishedRoutes}`); let errorResult = this.errorHandler.handleRouteError(e, req, res); if (typeof errorResult !== 'boolean' && typeof errorResult !== 'undefined' && errorResult !== null) errorResult = yield errorResult; if (initialStatusCode === res.statusCode) { this.logger.error(`Error handler did not send a response. Serving 500 - Internal server error`); res.status(http_status_type_1.HTTP_STATUS_INTERNAL_SERVER_ERROR); res.send('Internal server error'); } res.errorResult = errorResult; failed = true; throw e; } finally { --this.unfinishedRoutes; if (!failed && res.statusCode === initialStatusCode && !res.headersSent) { this.logger.error(`{${requestIndex}} route failed to send a response.`); let errorResult = this.errorHandler.handleNoRouteResponse(req, res); if (typeof errorResult !== 'boolean' && typeof errorResult !== 'undefined' && errorResult !== null) errorResult = yield errorResult; if (initialStatusCode === res.statusCode) { this.logger.error(`Error handler did not send a response. Serving 404 - Not Found`); res.status(http_status_type_1.HTTP_STATUS_NOT_FOUND); res.send(`Not found.`); } } this.logger.verbose(`{${requestIndex}} ending request. unfinishedRoutes: ${this.unfinishedRoutes}`); } }); }; let self = this; return function (req, res) { return __awaiter(this, void 0, void 0, function* () { let requestIndex = ++self.requestIndex; req.requestIndex = requestIndex; req.routeMethodName = routeMethodName; let interceptors = [...self._interceptors]; let interceptorCallbacks = []; let initialStatusCode = res.statusCode; for (let q = 0; q < interceptors.length; q++) { interceptorCallbacks.push(() => __awaiter(this, void 0, void 0, function* () { if (res.statusCode !== initialStatusCode || res.headersSent) return; yield interceptors[q + 1](req, res, interceptorCallbacks[q + 1]); })); } interceptors.push(() => __awaiter(this, void 0, void 0, function* () { yield fullRouterFn.call(self, requestIndex, req, res); })); yield interceptors[0](req, res, interceptorCallbacks[0]); }); }; } createPolicyResultsFn(policies, allResults) { let keys = policies.map(poli => poli[0]); return function (policyFn) { if (typeof policyFn === 'number') return allResults[policyFn]; for (let q = 0; q < keys.length; q++) { if (keys[q] === policyFn) return allResults[q]; } return undefined; }; } }; RouterReflector = RouterReflector_1 = __decorate([ injectable_decorator_1.Injectable({ provide: { useCallback: function (injector, logger, errorHandler, routerMeta, _router) { if (!routerMeta) return null; return new RouterReflector_1(injector, logger, errorHandler, routerMeta, _router); }, deps: [injector_1.Injector, logger_1.Logger, error_handler_1.ErrorHandler, router_1.RouterMetadata, router_service_1.RouterService], cache: true } }), name_decorator_1.Name('router'), __metadata("design:paramtypes", [injector_1.Injector, logger_1.Logger, error_handler_1.ErrorHandler, router_1.RouterMetadata, router_service_1.RouterService]) ], RouterReflector); exports.RouterReflector = RouterReflector; var RouterReflector_1; //# sourceMappingURL=reflector.js.map