UNPKG

@sauce-api/core

Version:

Sauce API core functionality

163 lines (162 loc) 7.08 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Routes = void 0; const utility_1 = require("../utility"); const Logger_1 = require("../Logger"); const chalk_1 = require("chalk"); const Sauce_1 = require("../Sauce"); const Policies_1 = require("./Policies"); const RouteValidator_1 = require("./RouteValidator"); /** * Manages the application's route configurations. * * @property {Array} routesArray - * It's strongly recommended to utilize the RouteDefinitions * file to manage your defined routes. This will make your * configured routes far more manageable and readable */ class Routes { constructor(config) { this.config = config; this.controllerMap = new Map(); this.routesArray = config.routes; } async getFormattedRoutes(stripHidden) { const formatted = await this.formatRoutes(this.routesArray, stripHidden); return this.sortRoutes(this.processRoutes(formatted)); } processRoutes(routeGroups) { return routeGroups.map((group) => { if (group.tag) { // Is a group of routes this.sortRoutes(group.routes); } return group; // Is a route obj }); } sortRoutes(routes) { return routes.sort((a, b) => { const route1 = a.path ? a.path.replace(/[.-]/, "") : ""; const route2 = b.path ? b.path.replace(/[.-]/, "") : ""; return route1 > route2 ? 1 : -1; }); } sortRouteGroups(routes) { return routes.sort((a, b) => { return a.tag < b.tag ? 1 : -1; }); } async formatRoutes(routes, stripHidden) { const groupedRoutes = new Map(); const ungroupedRoutes = []; await (0, utility_1.asyncForEach)(routes, async (route) => { if (!(stripHidden && route.hidden)) { const formattedObj = { method: route.method.toString(), path: route.path, controller: route.controller, action: route.action.toString(), policies: route.policies, description: route.description, isDeprecated: route.isDeprecated, pathParams: route.getFormattedPathParams(), queryParams: route.getFormattedQueryParams(), bodySchema: route.getFormattedBodySchema(), exampleResponse: route.exampleResponse }; if (formattedObj.pathParams) { this.cleanRouteParams(formattedObj.pathParams); } if (formattedObj.queryParams) { this.cleanRouteParams(formattedObj.queryParams); } if (formattedObj.bodySchema) { (0, utility_1.cleanObject)(formattedObj.bodySchema); } (0, utility_1.cleanObject)(formattedObj); if (typeof route.tag !== "undefined") { const tag = route.tag; if (groupedRoutes.has(tag)) { const group = groupedRoutes.get(tag); group.push(formattedObj); groupedRoutes.set(tag, group); } else { groupedRoutes.set(tag, [formattedObj]); } } else { ungroupedRoutes.push(formattedObj); } } }); const groupedRoutesArray = Array.from(groupedRoutes, ([tag, routes]) => ({ tag, routes })); return [...ungroupedRoutes, ...this.sortRouteGroups(groupedRoutesArray)]; } async cleanRouteParams(params) { await (0, utility_1.asyncForEach)(params, param => (0, utility_1.cleanObject)(param)); } addToControllerMap(route) { const path = this.getControllerPathFromRoute(route); const controller = require(path); this.controllerMap.set(path, controller); } getControllerPathFromRoute(route) { const extension = this.config.controllerExtension || "ts"; if (route.customControllerPath) { return `${this.config.controllerDirectory}/${route.customControllerPath}`; } return `${this.config.controllerDirectory}/${route.controller}/${route.controller}.controller.${extension}`; } /** This should only run on startup so it's fine that it's not async */ bindRoutes({ app, sauceCustom }) { this.routesArray.forEach(route => { try { if (route.excludedEnvironments && route.excludedEnvironments.includes(process.env.ENV)) { (0, Logger_1.log)(this.config, "info", (0, chalk_1.yellow)(`Excluding ${route.path} for this environment`)); } else { // Add the controlelr to the map for easy/efficient lookup later this.addToControllerMap(route); const policyList = (0, Policies_1.setPolicies)(this.config.policies); (0, Logger_1.log)(this.config, "debug", `Binding ${route.method.toString()} ${route.path}`); /** * @TODO * Add middleware support to routes */ app[route.method](route.path, async (req, res, next) => { /** * BEWARE, anything inside this block is inside of the * request itself. Avoid non async logic from here on */ try { let controller = this.controllerMap.get(this.getControllerPathFromRoute(route)); const sauce = (0, Sauce_1.buildSauceObj)({ config: this.config, req, res, next, app, currentRoute: route, custom: sauceCustom }); const controllerClassName = Object.keys(controller)[0]; controller = new controller[controllerClassName](sauce); const routeValidator = new RouteValidator_1.RouteValidator(sauce); await routeValidator.runValidations(); await routeValidator.checkPolicies(policyList); controller[route.action](req, res, next); } catch (e) { next(e); } }); } } catch (e) { (0, Logger_1.log)(this.config, "error", "FAILED BINDING ROUTE: ", e); throw e; } }); } } exports.Routes = Routes;