UNPKG

@zerooneit/expressive-tea

Version:
288 lines (287 loc) 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.View = exports.Middleware = exports.Param = exports.Delete = exports.Patch = exports.Put = exports.Post = exports.Get = exports.Route = void 0; const express_1 = require("express"); const lodash_1 = require("lodash"); const MetaData_1 = require("../classes/MetaData"); const RequestExceptions_1 = require("../exceptions/RequestExceptions"); const decorators_1 = require("../helpers/decorators"); const server_1 = require("../helpers/server"); const constants_1 = require("../libs/constants"); /** * @module Decorators/Router */ /** * Route Controller Decorator assign a router point into the module mount-point placeholder and is used just to create * a bucket to contains all the endpoints defined by method of the Controller. If mount-point is not defined automati- * cally this must considerate mounted on root, but is mandatory define the Controller with this decorator in order * to allow Expressive Tea Setting up the Controller as part of a Module. * * @decorator {ClassDecorator} Route - Assign a route to controller endpoints. * @summary Generate a Placeholder endpoint root for controller routes. * @param {string} mountpoint Register the url part to mount the Controller. * @example * {REPLACE-AT}Route('/) * class Example {} */ function Route(mountpoint = '/') { return (Route) => { return class ExpressiveTeaRoute extends Route { constructor(...args) { super(...args); const handlers = MetaData_1.default.get(constants_1.ROUTER_HANDLERS_KEY, this) || []; this.router = (0, express_1.Router)(); this.mountpoint = mountpoint; (0, lodash_1.each)(handlers, h => { const middlewares = h.handler.$middlewares || []; this.router[h.verb](h.route, ...middlewares, this.__registerHandler(h)); }); } __mount(parent) { const rootMiddlewares = MetaData_1.default.get(constants_1.ROUTER_MIDDLEWARES_KEY, this) || []; parent.use(this.mountpoint, ...rootMiddlewares, this.router); return this; } __registerHandler(options) { const self = this; const decoratedArguments = MetaData_1.default.get(constants_1.ARGUMENTS_KEY, options.target, options.propertyKey); const annotations = MetaData_1.default.get(constants_1.ROUTER_ANNOTATIONS_KEY, options.target, options.propertyKey); return async function exec(request, response, next) { try { let isNextUsed = false; const nextWrapper = () => (error) => { next(error); isNextUsed = true; }; // TODO: Must be Depecrated in prior version. const result = await options.handler.apply(self, (0, server_1.mapArguments)(decoratedArguments, request, response, nextWrapper())); if (!response.headersSent && !isNextUsed) { (0, server_1.autoResponse)(request, response, annotations, result); } } catch (e) { if (e instanceof RequestExceptions_1.GenericRequestException) { return next(e); } next(new RequestExceptions_1.GenericRequestException(e.message || 'System Error')); } }; } }; }; } exports.Route = Route; /** * Define a GET Endpoint response over the controller router and can define in which route should be responds and * by default this is responding to everything on the controller root path. * @decorator {MethodDecorator} Get - Create an GET Response over the designed route. * @summary Define a GET Controller Endpoint on Controller. * @param {string} route URL Part to mount create a handler. * @example * {REPLACE-AT}Route('/') * class GenericController { * {REPLACE-AT}Get() // This Response to all GET Requests for controller route. * methodError() {} * * {REPLACE-AT}Get('/data') // This Response to "/data" GET Requests for controller route. * methodData() {} * } * */ function Get(route = '*') { return (0, server_1.generateRoute)(route, 'get'); } exports.Get = Get; /** * Define a POST Endpoint response over the controller router and can define in which route should be responds and * by default this is responding to everything on the controller root path. * @decorator {MethodDecorator} POST - Create an POST Response over the designed route. * @summary Define a POST Controller Endpoint on Controller. * @param {string} route URL Part to mount create a handler. * @example * {REPLACE-AT}Route('/') * class GenericController { * {REPLACE-AT}Post() // This Response to all GET Requests for controller route. * methodError() {} * * {REPLACE-AT}Post('/data') // This Response to "/data" GET Requests for controller route. * methodData() {} * } * */ function Post(route = '*') { return (0, server_1.generateRoute)(route, 'post'); } exports.Post = Post; /** * Define a PUT Endpoint response over the controller router and can define in which route should be responds and * by default this is responding to everything on the controller root path. * @decorator {MethodDecorator} Put - Create an PUT Response over the designed route. * @summary Define a PUT Controller Endpoint on Controller. * @param {string} route URL Part to mount create a handler. * @example * {REPLACE-AT}Route('/') * class GenericController { * {REPLACE-AT}Put() // This Response to all PUT Requests for controller route. * methodError() {} * * {REPLACE-AT}Put('/data') // This Response to "/data" PUT Requests for controller route. * methodData() {} * } * */ function Put(route = '*') { return (0, server_1.generateRoute)(route, 'put'); } exports.Put = Put; /** * Define a PATCH Endpoint response over the controller router and can define in which route should be responds and * by default this is responding to everything on the controller root path. * @decorator {MethodDecorator} Patch - Create an PATCH Response over the designed route. * @summary Define a PATCH Controller Endpoint on Controller. * @param {string} route URL Part to mount create a handler. * @example * {REPLACE-AT}Route('/') * class GenericController { * {REPLACE-AT}Patch() // This Response to all PATCH Requests for controller route. * methodError() {} * * {REPLACE-AT}Patch('/data') // This Response to "/data" PATCH Requests for controller route. * methodData() {} * } * */ function Patch(route = '*') { return (0, server_1.generateRoute)(route, 'patch'); } exports.Patch = Patch; /** * Define a DELETE Endpoint response over the controller router and can define in which route should be responds and * by default this is responding to everything on the controller root path. * @decorator {MethodDecorator} Delete - Create an DELETE Response over the designed route. * @summary Define a DELETE Controller Endpoint on Controller. * @param {string} route URL Part to mount create a handler. * @example * {REPLACE-AT}Route('/') * class GenericController { * {REPLACE-AT}Delete() // This Response to all DELETE Requests for controller route. * methodError() {} * * {REPLACE-AT}Delete('/data') // This Response to "/data" DELETE Requests for controller route. * methodData() {} * } * */ function Delete(route = '*') { return (0, server_1.generateRoute)(route, 'delete'); } exports.Delete = Delete; /** * Define a Route path parameter transformation method as mentioned on Express this is just call by controller endpoints * route paths and it helps to define logic on each route path parameters. Example, you can require transform userId * parameter and added to request object. * * The method to response this must follow the Express callback notation (Request, Response, Next, paramValue) which * also must be use Next method to allow continue the process flow from Express. * @decorator {MethodDecorator} Param - Create a transformation middleware for router parameters. * @summary Define a Parameter transformation on router path.. * @param {string} route URL Part to mount create a handler. * @example * {REPLACE-AT}Route('/') * class GenericController { * {REPLACE-AT}Param('userId') // This Response to all GET Requests for controller route. * async methodError(req, res, next, param, id) { * const user = await User.find(id) * try { * if(!user) throw new Error('User not Found'); * * req.user = user; * next(); * } catch(e) { * next(e); * } * } * * {REPLACE-AT}Get('/:userId') // This Response to "/:userId" GET Requests for controller route. * methodData(req, res) { * res.json(req.user); * } * } * */ function Param(route = '*') { return (0, server_1.generateRoute)(route, 'param'); } exports.Param = Param; /** * Middleware Decorator is following the middleware functionality inheritance from Express framework itself and follow * the same rules, that execute any code before response the request and can change the current request or response * object, and requires use next callback to pass control to next middleware. * * This define the middlewares at Controller and Controller methods level, for application levels please see the * Plug Decorator which can be useful to declare at that level.. * @decorator {MethodDecorator|ClassDecorator} Middleware - Attach a middleware definition on the root route or endpoint * routes. * @summary Assign a middleware to the Controller or Method route path flow. * @example * {REPLACE-AT}Middleware((req, res, next) { * req.controllerName = 'Example'; * next(); * }) * class ExampleController { * {REPLACE-AT}Get('/someResponse') * {REPLACE-AT}Middleware((req, res, next) { * req.methodName = 'someResponse'; * next(); * }) * someRespone(req, res) { * res.send(`${req.controllerName}:${req.methodName}`); * } * } * * @param {Function} middleware Register a middleware over router. */ function Middleware(middleware) { return (target, property, descriptor) => { if (!property) { rootMiddleware(target, middleware); } else { routeMiddleware(target, descriptor, middleware); } }; } exports.Middleware = Middleware; /** * Will generate a GET route to respond rendering a view template setting up before; the controller method <b>MUST</b> * return an object in order to fulfilled the local variables into the view. * @decorator {MethodDecorator} View - Create a Vew Response over the designed route. * @summary Define a View response endpoint on Controller. * @param {string} viewName - View render layout name defined by configuration. * @param {string} route - URL Part to mount create a handler. * @return {object} Controller must return and object as local variables for the view. * @example * {REPLACE-AT}Route('/') * class GenericController { * {REPLACE-AT}View('login', '/view') // This Response to all GET Requests for controller route. * methodError() {} * } * */ function View(viewName, route) { route = route || `/${viewName}`; return (target, propertyKey, descriptor) => { (0, decorators_1.addAnnotation)('view', target, propertyKey, viewName); (0, server_1.router)('get', route, target, descriptor.value, propertyKey); }; } exports.View = View; function rootMiddleware(target, middleware) { const existedRoutesHandlers = MetaData_1.default.get(constants_1.ROUTER_MIDDLEWARES_KEY, target) || []; existedRoutesHandlers.unshift(middleware); MetaData_1.default.set(constants_1.ROUTER_MIDDLEWARES_KEY, existedRoutesHandlers, target); } function routeMiddleware(target, descriptor, middleware) { descriptor.value.$middlewares = descriptor.value.$middlewares || []; descriptor.value.$middlewares.unshift(middleware); }