miter
Version:
A typescript web framework based on ExpressJs based loosely on SailsJs
380 lines • 19.4 kB
JavaScript
"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 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