UNPKG

5.54 kBJavaScriptView Raw
1const fs = require('fs');
2const path = require('path');
3const { chalk } = require('./../namespace/console');
4const policy = require('./policy');
5const bodyParser = require('body-parser');
6const args = require('optimist').argv;
7const lodash = require('lodash');
8const debug = require('debug');
9
10module.exports = class Router {
11
12 constructor (project, server) {
13 this.debug = debug('glad');
14 this.debug('Router:constructor');
15 this.project = project;
16 this.server = server;
17 this.routes = {};
18 this.controllers = {};
19 this.models = {};
20 this.errors = [];
21 this.config = require(this.project.configPath);
22 this.policies = require(path.join(project.projectPath, "policies.js"));
23 this.logging = require(project.configPath).logHTTP;
24 }
25
26 /**
27 * Builds out the router object
28 */
29 buildRoutes () {
30 this.debug('Router:buildRoutes');
31 return new Promise( (resolve, reject) => {
32 this.setRoutes();
33 this.setControllers();
34 this.setModels();
35 if (this.errors.length) {
36 reject(this.errors);
37 } else {
38 resolve();
39 }
40 });
41 }
42
43 /**
44 * Requires all of the routes for the application and stores them at Router.routes
45 */
46 setRoutes () {
47 this.debug('Router:setRoutes');
48 this.setObjectsForSegment(this.project.routesPath, 'routes', 'Router');
49 }
50
51 /**
52 * Requires all of the models for the application and stores them at Router.models
53 */
54 setModels () {
55 this.debug('Router:setModels');
56 this.setObjectsForSegment(this.project.modelsPath, 'models', 'Model');
57 }
58
59 /**
60 * Requires all of the controllers for the application and stores them at Router.controllers
61 */
62 setControllers () {
63 this.debug('Router:setControllers');
64 this.setObjectsForSegment(this.project.controllersPath, 'controllers', 'Controller');
65 }
66
67 /**
68 * Gets the file paths for all of the .js files in a directory [path]
69 */
70 getFilesForPath (path) {
71 this.debug(`Router:getFilesForPath - gets all js files in ${path}`);
72 let paths = fs.readdirSync(path).filter(file => /(\.js)$/.test(file));
73 let only = args.only && args.only.split(',').map(x => `${x}.js`);
74 /**
75 * Implement the --only flag.
76 * This allows you to have the server only handle routes for a specific controller.
77 * This use case is primarily for occasions such as booting up a server to warm caches for
78 * a specific controller, or for adding additional servers to handle an impacted resource.
79 **/
80 if (only && only.length) {
81 paths = paths.filter( p => lodash.includes(only, p));
82 }
83 return paths;
84 }
85
86 /**
87 * Scans a directory for .js files,
88 * Requires all of the js files in `fpath` to Router[segment]
89 * fpath fully qualified path to a folder
90 * segment `routes` | `models` | `controllers`
91 * error The humanized name of the segment that is being created
92 */
93 setObjectsForSegment (fpath, segment, error) {
94 this.debug(`Router:setObjectsForSegment ${segment}`);
95 this.getFilesForPath(fpath).forEach(file => {
96 let ref = path.join (fpath, file);
97 try {
98 this[segment][file.replace('.js', '')] = require(ref);
99 } catch (err) {
100 chalk.error(`Glad > ${error}`, err.stack);
101 this.errors.push({
102 message : "> Could Not Bind " + ref.split('/').pop().toString() + '::' + file.replace('.js', '') + " To Any Route!",
103 err : err
104 });
105 }
106 });
107 }
108
109 /**
110 * Implement the default body parser, override the default if the bodyParser key is defined on the route
111 * - Allow the engineer to override the default options but still use the default parser.
112 * - Allow the engineer to override the parser type but still use the default options.
113 * - Allow the engineer to override both the parser and the options.
114 */
115 bodyParser (config) {
116 this.debug('Router:bodyParser');
117 let type, options;
118 if (config.bodyParser && config.bodyParser.custom) {
119 return config.bodyParser.custom;
120 } else if (config.bodyParser) {
121 type = config.bodyParser.parser || this.config.defaultBodyParser && this.config.defaultBodyParser.type;
122 options = config.bodyParser || this.config.defaultBodyParser;
123 } else {
124 type = this.config.defaultBodyParser && this.config.defaultBodyParser.type;
125 options = this.config.defaultBodyParser;
126 }
127 type = type || 'json';
128 options = options || {};
129 return bodyParser[type](options);
130 }
131
132 setViewPath (config) {
133 this.debug('Router:setViewPath');
134 return (req, res, next) => {
135 req.__rootViewPath = `${this.project.viewsPath}`;
136 req.viewPath = req.__rootViewPath + config.action;
137 next();
138 }
139 }
140
141 /**
142 * Create the route in express
143 * - Get a body parser
144 * - Register the route and send the request through the body-parser then check the policy then finally the controller.
145 */
146 route (method, config) {
147 this.debug(`Router:route ${config.path} => ${config.controller.name}#${config.action}`);
148 let { path, controller, action } = config;
149 let bodyParser = this.bodyParser(config);
150 let viewPath = this.setViewPath(config);
151 method = method.toLowerCase();
152 return this.server.app[method](path, bodyParser, viewPath, (req, res) => {
153 debug('glad')('Router:apply %s => %s#%s with policy %s', path, controller.name, action, config.policy || 'none');
154 return new policy(this.policies, this.logging, this.server, config.policy, controller, action).restrict(req, res);
155 });
156 }
157
158}