"use strict"; import appolo = require('appolo'); import _ = require('lodash'); import joi = require('joi'); import router from './router'; import {IRouteInnerOptions, IRouteOptions} from "../interfaces/IRouteOptions"; import pathToRegexp = require('path-to-regexp'); import {MiddlewareHandler, NextFn} from "../app/app"; import {IMiddleware, IMiddlewareCtr} from "../interfaces/IMiddleware"; import {IResponse} from "../app/response"; import {IRequest} from "../app/request"; import {IController, IControllerCtr} from "../controller/IController"; import {Util} from "../util/util"; import {Methods} from "../common/enums/methods"; import {RouteModel} from "./routeModel"; let orderIndex = 0; export class Route { protected _route: IRouteInnerOptions; constructor(controller: string | { new(): IController }) { this._route = { middlewareHandler: [], regExp: null, methodUpperCase: 'GET', route: { method: 'get', roles: [], environments: [], middleware: [], validations: {}, controller: _.isFunction(controller) && controller.name ? _.camelCase(controller.name) : controller, path: "", order: orderIndex++, params: {}, action: null, } }; router.getRoutes().push(this._route); } public path(pathPattern: string): this { this._route.route.path = pathPattern; let keys = []; this._route.regExp = pathToRegexp(Util.addSlashEnd(this._route.route.path), keys); this._route.paramsKeys = keys; if (pathPattern == "/") { this.order(999998) } else if (pathPattern == "*") { this.order(999999); this._route.regExp = new RegExp(".*") } return this; } public order(order: number): this { this._route.route.order = order; return this } public action(action: ((c: T) => Function) | string): this { this._route.route.action = action; return this; } public abstract(abstract: Partial): this { let items = _.pick(abstract, ["environments", "roles", "middleware", "validations", "convertToCamelCase", "method", "params"]); _.forEach(items, (item: any, key: string) => { this[key](item); }); return this; } public extend(opts: { [index: string]: any }): this { _.extend(this._route.route, opts); return this; } public param(key: string, value: any): this { this._route.route.params[key] = value; return this } public validation(key: string | { [index: string]: joi.Schema }| RouteModel, validation?: joi.Schema): this { return this.validations(key, validation); } public validations(key: string | { [index: string]: joi.Schema }| RouteModel, validation?: joi.Schema): this { if (key.constructor && key.constructor.prototype === RouteModel.constructor.prototype && (key as any).prototype && (key as any).prototype.__validations__) { key = (key as any).prototype.__validations__ } if (_.isObject(key)) { _.extend(this._route.route.validations, key) } else { this._route.route.validations[key as string] = validation } return this; } public method(method: 'get' | 'post' | 'delete' | 'patch' | 'head' | 'put' | Methods): this { this._route.route.method = method; this._route.methodUpperCase = method.toUpperCase(); return this; } public environment(environment: string | string[]): this { return this.environments(environment) } public environments(environment: string | string[]): this { if (_.isArray(environment)) { this._route.route.environments.push.apply(this._route.route.environments, environment); } else { this._route.route.environments.push(environment) } return this; } public convertToCamelCase(value: boolean): this { this._route.route.convertToCamelCase = value; return this } public middleware(middleware: string | MiddlewareHandler | IMiddlewareCtr | ((req: any, res: any, next: any) => void), order: "head" | "tail" = "tail"): this { let arrMethod = order == "head" ? "unshift" : "push"; if (_.isArray(middleware)) { return this.middlewares(middleware, order) } let middle: any = middleware; if (_.isPlainObject(middle) && (middle.order && middle.middleware)) { this.middleware(middle.middleware, middle.order) } this._route.route.middleware.push(middleware); if (Util.isClass(middleware)) { middleware = _.camelCase((middleware as IMiddlewareCtr).name) } else if (typeof middleware == "function") { this._route.middlewareHandler[arrMethod](middleware as MiddlewareHandler); } if (typeof middleware == "string") { middleware = (function (middlewareId): MiddlewareHandler { return function (req: IRequest, res: IResponse, next: NextFn) { let middleware: IMiddleware = appolo.inject.getObject(middlewareId, [req, res, next, req.$route]); if (!middleware) { throw new Error("failed to find middleware " + middleware); } middleware.run(req, res, next, req.$route.route); } })(middleware); this._route.middlewareHandler[arrMethod](middleware as MiddlewareHandler); } return this; } public middlewares(middlewares: string[] | MiddlewareHandler[] | IMiddlewareCtr[], order: "head" | "tail" = "tail"): this { _.forEach(_.isArray(middlewares) ? middlewares : [middlewares], fn => this.middleware(fn as any, order)); return this; } public role(role: string | string[]): this { return this.roles(role) } public roles(role: string | string[]): this { if (_.isArray(role)) { this._route.route.roles.push.apply(this._route.route.roles, role); } else { this._route.route.roles.push(role) } return this; } route(controller: string | IControllerCtr): Route { return new Route(controller || this._route.route.controller); } } export default function (controller: string | IControllerCtr): Route { return new Route(controller) }