@sauce-api/core
Version:
Sauce API core functionality
163 lines (162 loc) • 7.08 kB
JavaScript
;
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;