nodester 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +33 -39
- package/lib/application/index.js +110 -38
- package/lib/constants/Operations.js +23 -0
- package/lib/controllers/methods/index.js +194 -0
- package/lib/controllers/mixins/index.js +222 -0
- package/lib/database/connection.js +34 -0
- package/lib/database/migration.js +42 -0
- package/lib/database/utils.js +19 -0
- package/lib/enums/Enum.js +16 -0
- package/lib/facades/methods/index.js +173 -0
- package/lib/facades/mixins/index.js +111 -0
- package/lib/factories/errors/CustomError.js +7 -5
- package/lib/factories/responses/html.js +7 -2
- package/lib/factories/responses/rest.js +110 -0
- package/lib/http/codes/index.js +157 -0
- package/lib/{application/http → http}/request.js +6 -30
- package/lib/{application/http → http}/response.js +20 -53
- package/lib/middlewares/etag/index.js +62 -0
- package/lib/middlewares/ql/sequelize/index.js +34 -0
- package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +121 -0
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +456 -0
- package/lib/models/associate.js +17 -0
- package/lib/models/define.js +56 -14
- package/lib/models/mixins.js +100 -78
- package/lib/params/Params.js +37 -0
- package/lib/queries/Colander.js +84 -0
- package/lib/queries/NodesterQueryParams.js +139 -0
- package/lib/queries/traverse.js +311 -0
- package/lib/router/handlers.util.js +81 -0
- package/lib/router/index.js +441 -0
- package/lib/router/route.js +124 -0
- package/lib/router/routes.util.js +85 -0
- package/lib/router/utils.js +30 -0
- package/lib/stacks/MarkersStack.js +35 -0
- package/lib/{application → stacks}/MiddlewareStack.js +47 -13
- package/lib/utils/path.util.js +3 -1
- package/lib/utils/types.util.js +51 -1
- package/lib/validators/dates.js +25 -0
- package/lib/validators/numbers.js +14 -0
- package/package.json +31 -4
- package/tests/index.test.js +7 -2
- package/tests/nql.test.js +277 -0
- package/docs/App.md +0 -13
- package/docs/Queries.md +0 -61
- package/docs/Readme.md +0 -2
- package/docs/Routing.md +0 -34
- package/examples/goal/index.js +0 -23
- package/examples/rest/index.js +0 -25
- package/examples/rest/node_modules/.package-lock.json +0 -40
- package/examples/rest/package-lock.json +0 -72
- package/examples/rest/package.json +0 -14
- package/lib/constants/ConstantsEnum.js +0 -13
- package/lib/controllers/Controller.js +0 -474
- package/lib/controllers/JWTController.js +0 -240
- package/lib/controllers/ServiceController.js +0 -109
- package/lib/controllers/WebController.js +0 -75
- package/lib/facades/Facade.js +0 -388
- package/lib/facades/FacadeParams.js +0 -11
- package/lib/facades/ServiceFacade.js +0 -17
- package/lib/facades/jwt.facade.js +0 -273
- package/lib/factories/responses/api.js +0 -90
- package/lib/models/DisabledRefreshToken.js +0 -68
- package/lib/models/Extractor.js +0 -320
- package/lib/routers/Default/index.js +0 -143
- package/lib/routers/Default/layer.js +0 -50
- package/lib/routers/Main/index.js +0 -10
- package/lib/routers/Roles/index.js +0 -81
- package/lib/utils/params.util.js +0 -19
- /package/lib/{application/http → http}/utils.js +0 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const MiddlewareStack = require('../stacks/MiddlewareStack');
|
|
9
|
+
const MarkersStack = require('../stacks/MarkersStack');
|
|
10
|
+
const Route = require('./route');
|
|
11
|
+
// Utils:
|
|
12
|
+
const { typeOf } = require('../utils/types.util');
|
|
13
|
+
const { wrapRouteHandler } = require('./routes.util');
|
|
14
|
+
const { parseProviderFileNames } = require('./utils');
|
|
15
|
+
// File system:
|
|
16
|
+
const Path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const commonExtensions = require('common-js-file-extensions');
|
|
19
|
+
// Debug & console:
|
|
20
|
+
const consl = require('../logger/console');
|
|
21
|
+
const debug = require('debug')('nodester:router');
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
module.exports = class NodesterRouter {
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initialize a new `NodesterRouter`.
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} opts
|
|
30
|
+
* - {Array} codeFileExtensions
|
|
31
|
+
* - {String} controllersPath
|
|
32
|
+
* - {Object} controllers
|
|
33
|
+
* - {Boolean} finalhandlerEnabled
|
|
34
|
+
*
|
|
35
|
+
* @api public
|
|
36
|
+
*/
|
|
37
|
+
constructor(opts={}) {
|
|
38
|
+
// Reference to the controllers stack.
|
|
39
|
+
this._controllers = new Map();
|
|
40
|
+
|
|
41
|
+
// Reference to middlewares stack.
|
|
42
|
+
this._middlewares = new MiddlewareStack({ finalhandlerEnabled: !!opts.finalhandlerEnabled });
|
|
43
|
+
|
|
44
|
+
// Reference to the markers stack.
|
|
45
|
+
this._markers = new MarkersStack();
|
|
46
|
+
|
|
47
|
+
// Reference to the providers stack.
|
|
48
|
+
this._providers = new Map();
|
|
49
|
+
|
|
50
|
+
this.codeFileExtensions = commonExtensions.code;
|
|
51
|
+
this.paths = {};
|
|
52
|
+
|
|
53
|
+
// Indicates whether we can add more middlewares or no.
|
|
54
|
+
this.isLocked = false;
|
|
55
|
+
|
|
56
|
+
// Options:
|
|
57
|
+
// if "codeFileExtensions" was set:
|
|
58
|
+
if (!!opts.codeFileExtensions && Array.isArray(opts.codeFileExtensions)) {
|
|
59
|
+
this.codeFileExtensions = [...opts.codeFileExtensions];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// If "controllersPath" was set,
|
|
63
|
+
// cache all controllers in directory:
|
|
64
|
+
if (!!opts.controllersPath) {
|
|
65
|
+
// Save path.
|
|
66
|
+
this.paths.controllers = opts.controllersPath;
|
|
67
|
+
|
|
68
|
+
// Only get files, which have available file extensions:
|
|
69
|
+
const availableFileExtensions = this.codeFileExtensions;
|
|
70
|
+
const fileNames = fs.readdirSync(this.paths.controllers);
|
|
71
|
+
|
|
72
|
+
const controllersNames = parseProviderFileNames(fileNames, availableFileExtensions, 'controller');
|
|
73
|
+
|
|
74
|
+
for (const { fileName, controllerName } of controllersNames) {
|
|
75
|
+
const controller = require(Path.join(this.paths.controllers, fileName));
|
|
76
|
+
this.addController(controller, controllerName);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If "controllers" were provided as an Object:
|
|
81
|
+
if (!!opts.controllers) {
|
|
82
|
+
if (typeOf(opts.controllers) !== 'Object') {
|
|
83
|
+
const err = new TypeError(`"controllers" must be an Object.`);
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const entities = Object.entities(opts.controllers);
|
|
88
|
+
for (const [controllerName, controllerDefinition] of entities) {
|
|
89
|
+
this.addController(controllerDefinition, controllerName);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// If "providersPath" was set,
|
|
94
|
+
// cache all providers in directory:
|
|
95
|
+
if (!!opts.providersPath) {
|
|
96
|
+
// Save path.
|
|
97
|
+
this.paths.providers = opts.providersPath;
|
|
98
|
+
|
|
99
|
+
// Only get files, which have available file extensions:
|
|
100
|
+
const availableFileExtensions = this.codeFileExtensions;
|
|
101
|
+
const fileNames = fs.readdirSync(this.paths.providers);
|
|
102
|
+
|
|
103
|
+
const providersNames = parseProviderFileNames(fileNames, availableFileExtensions, 'provider');
|
|
104
|
+
|
|
105
|
+
for (const { fileName, providerName } of providersNames) {
|
|
106
|
+
const provider = require(Path.join(this.paths.providers, fileName));
|
|
107
|
+
this.addProvider(provider, providerName);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// If "providers" were provided as an Object:
|
|
112
|
+
if (!!opts.providers) {
|
|
113
|
+
if (typeOf(opts.providers) !== 'Object') {
|
|
114
|
+
const err = new TypeError(`"providers" must be an Object.`);
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const entities = Object.entities(opts.providers);
|
|
119
|
+
for (const [providerName, providerDefinition] of entities) {
|
|
120
|
+
this.addProvider(providerDefinition, providerName);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
* Adds:
|
|
128
|
+
* - (controller) new controller to the stack;
|
|
129
|
+
* - (middleware) new middleware to the stack;
|
|
130
|
+
* - (marker) new marker to the stack;
|
|
131
|
+
* - (route) new route to the stack;
|
|
132
|
+
*
|
|
133
|
+
* @api public
|
|
134
|
+
*/
|
|
135
|
+
get add() {
|
|
136
|
+
return {
|
|
137
|
+
controller: this.addController.bind(this),
|
|
138
|
+
middleware: this.addMiddleware.bind(this),
|
|
139
|
+
marker: this.addMarker.bind(this),
|
|
140
|
+
provider: this.addProvider.bind(this),
|
|
141
|
+
route: this.addRoute.bind(this),
|
|
142
|
+
routes: this.addRoutes.bind(this),
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* Adds new controller to the controllers stack.
|
|
149
|
+
*
|
|
150
|
+
* @param {Function|Object} controller
|
|
151
|
+
* @param {String} controllerName
|
|
152
|
+
*
|
|
153
|
+
* @api public
|
|
154
|
+
*/
|
|
155
|
+
addController(fnOrObject, controllerName=null) {
|
|
156
|
+
const controllerType = typeOf(fnOrObject);
|
|
157
|
+
const name = controllerName ?? fnOrObject?.name ?? fnOrObject.constructor.name;
|
|
158
|
+
|
|
159
|
+
// If controller was exported as Object:
|
|
160
|
+
if (controllerType === 'Object') {
|
|
161
|
+
this._controllers.set(name, fnOrObject);
|
|
162
|
+
}
|
|
163
|
+
// If controller was exported as a constructor function:
|
|
164
|
+
else if (controllerType === 'function') {
|
|
165
|
+
this._controllers.set(name, new fnOrObject());
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const err = new TypeError(`Please check how you exported ${ name }, it should be either Object or constructor function.`);
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
* Adds new middleware to the stack.
|
|
176
|
+
*
|
|
177
|
+
* @param {Function} fn
|
|
178
|
+
* @param {Integer} index (0 or undefined)
|
|
179
|
+
*
|
|
180
|
+
* @return {Integer} index of new middleware
|
|
181
|
+
*
|
|
182
|
+
* @api public
|
|
183
|
+
*/
|
|
184
|
+
addMiddleware(fn, index) {
|
|
185
|
+
return this._middlewares.add(fn, index);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
/*
|
|
190
|
+
* Adds new marker to the stack.
|
|
191
|
+
*
|
|
192
|
+
* @param {String} markerName
|
|
193
|
+
* @param {Function} fn
|
|
194
|
+
*
|
|
195
|
+
* @api public
|
|
196
|
+
*/
|
|
197
|
+
addMarker(markerName='', fn) {
|
|
198
|
+
if (markerName.length > 0 && markerName.indexOf('/') > -1) {
|
|
199
|
+
const err = new TypeError(`"markerName" can't contain slashes "/"`);
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (this._markers.get(markerName)) {
|
|
204
|
+
const err = new Error(`Marker with name ${ markerName } is already set.`);
|
|
205
|
+
throw err;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this._markers.add(markerName, fn);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
/*
|
|
213
|
+
* Adds new provider to the providers stack.
|
|
214
|
+
*
|
|
215
|
+
* @param {Function|Object} provider
|
|
216
|
+
* @param {String} providerName
|
|
217
|
+
*
|
|
218
|
+
* @api public
|
|
219
|
+
*/
|
|
220
|
+
addProvider(fnOrObject, providerName=null) {
|
|
221
|
+
const providerType = typeOf(fnOrObject);
|
|
222
|
+
const name = providerName ?? fnOrObject?.name ?? fnOrObject.constructor.name;
|
|
223
|
+
|
|
224
|
+
// If provider was exported as Object:
|
|
225
|
+
if (providerType === 'Object') {
|
|
226
|
+
this._providers.set(name, fnOrObject);
|
|
227
|
+
}
|
|
228
|
+
// If provider was exported as a constructor function:
|
|
229
|
+
else if (providerType === 'function') {
|
|
230
|
+
this._providers.set(name, new fnOrObject());
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
const err = new TypeError(`Please check how you exported ${ name }, it should be either Object or constructor function.`);
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
/*
|
|
240
|
+
* Creates route middleware and adds it to the stack.
|
|
241
|
+
*
|
|
242
|
+
* @param {String} route
|
|
243
|
+
* @param {Object|Function} handler
|
|
244
|
+
*
|
|
245
|
+
* @api public
|
|
246
|
+
*/
|
|
247
|
+
addRoute(route='', handler) {
|
|
248
|
+
const parsed = new Route(route);
|
|
249
|
+
const handlerType = typeOf(handler);
|
|
250
|
+
|
|
251
|
+
// ToDo: move it to separate validator:
|
|
252
|
+
if (parsed.method === undefined) {
|
|
253
|
+
const err = new TypeError(`"route" should start with one of the following methods: [GET, POST, PUT, DELETE, QUERY, HEADER, OPTIONS]`);
|
|
254
|
+
throw err;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (handlerType === 'Object' && !this.paths.controllers && !this.paths.providers) {
|
|
258
|
+
const msg = `Please set "controllersPath" or "providersPath" during Router initialization.`;
|
|
259
|
+
const err = new TypeError(msg);
|
|
260
|
+
throw err;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const wrapped = wrapRouteHandler.call(this, parsed, handler);
|
|
264
|
+
return this.addMiddleware(wrapped);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
/*
|
|
269
|
+
* Loops through provided "routes" object
|
|
270
|
+
* and adds them throught "addRoute".
|
|
271
|
+
*
|
|
272
|
+
* @param {Object} routes
|
|
273
|
+
*
|
|
274
|
+
* @api public
|
|
275
|
+
*/
|
|
276
|
+
addRoutes(routes={}) {
|
|
277
|
+
for (const [route, handler] of Object.entries(routes)) {
|
|
278
|
+
this.addRoute(route, handler);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
/*
|
|
284
|
+
* Proxy to .add.middleware()
|
|
285
|
+
*
|
|
286
|
+
* @param {Function} fn
|
|
287
|
+
*
|
|
288
|
+
* @api public
|
|
289
|
+
*/
|
|
290
|
+
use(fn) {
|
|
291
|
+
return this.add.middleware(fn);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
/*
|
|
296
|
+
* Adds middlewares, which only fires for set marker.
|
|
297
|
+
*
|
|
298
|
+
* @param {String} markerName
|
|
299
|
+
*
|
|
300
|
+
* @api public
|
|
301
|
+
*/
|
|
302
|
+
only(markerName='') {
|
|
303
|
+
const self = this;
|
|
304
|
+
const markerFn = this._markers.get(markerName);
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
route: (route, fn) => {
|
|
308
|
+
const parsed = new Route(route);
|
|
309
|
+
// ToDo: move it to separate validator:
|
|
310
|
+
if (parsed.method === undefined) {
|
|
311
|
+
const err = new TypeError(`"route" should start with one of the following methods: [GET, POST, PUT, DELETE, QUERY, HEADER, OPTIONS]`);
|
|
312
|
+
throw err;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const wrapped = async (req, res, next) => {
|
|
316
|
+
const matched = await markerFn.call(self, req, res);
|
|
317
|
+
// Skip, if marker's condition was not matched:
|
|
318
|
+
if (!matched) {
|
|
319
|
+
return next();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Wrap and call:
|
|
323
|
+
const routeHandler = wrapRouteHandler.call(self, parsed, fn);
|
|
324
|
+
await routeHandler.call(self, req, res, next);
|
|
325
|
+
|
|
326
|
+
// If response was not sent,
|
|
327
|
+
// go to next one:
|
|
328
|
+
if (res.headersSent === false) {
|
|
329
|
+
next();
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
return self.add.route(wrapped);
|
|
333
|
+
},
|
|
334
|
+
use: (fn) => {
|
|
335
|
+
const wrapped = async (req, res, next) => {
|
|
336
|
+
const matched = await markerFn.call(self, req, res);
|
|
337
|
+
// Skip, if marker's condition was not matched:
|
|
338
|
+
if (!matched) {
|
|
339
|
+
return next();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
await fn.call(self, req, res, next);
|
|
343
|
+
|
|
344
|
+
// If response was not sent,
|
|
345
|
+
// go to next one:
|
|
346
|
+
if (res.headersSent === false) {
|
|
347
|
+
next();
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
return self.use(wrapped);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Handles server request.
|
|
358
|
+
*
|
|
359
|
+
* @api public
|
|
360
|
+
*/
|
|
361
|
+
handle(req, res, next) {
|
|
362
|
+
return this._middlewares.process(req, res, next)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
/*
|
|
367
|
+
* Prepare router for processing.
|
|
368
|
+
*
|
|
369
|
+
* @api public
|
|
370
|
+
*/
|
|
371
|
+
lock() {
|
|
372
|
+
// Stack is ready.
|
|
373
|
+
this.isLocked = true;
|
|
374
|
+
|
|
375
|
+
debug(`router is locked`);
|
|
376
|
+
|
|
377
|
+
this._middlewares.lock();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
/*
|
|
382
|
+
* Unlocks router.
|
|
383
|
+
*
|
|
384
|
+
* @api public
|
|
385
|
+
*/
|
|
386
|
+
unlock() {
|
|
387
|
+
this.isLocked = false;
|
|
388
|
+
|
|
389
|
+
debug(`router is unlocked`);
|
|
390
|
+
|
|
391
|
+
this._middlewares.unlock();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
/*
|
|
396
|
+
* Removes something
|
|
397
|
+
*/
|
|
398
|
+
get remove() {
|
|
399
|
+
return {
|
|
400
|
+
middleware: this.removeMiddleware.bind(this)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Removes middleware at index.
|
|
407
|
+
*
|
|
408
|
+
* @param {Integer} index
|
|
409
|
+
*
|
|
410
|
+
* @return {Integer} middlewares stack length
|
|
411
|
+
*
|
|
412
|
+
* @api public
|
|
413
|
+
*/
|
|
414
|
+
removeMiddleware(index=-1) {
|
|
415
|
+
this._middlewares.remove(index);
|
|
416
|
+
return this._middlewares.length;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Extends Router & makes sure, that "key" param is not present already.
|
|
423
|
+
*
|
|
424
|
+
* @param {String} key
|
|
425
|
+
* @param {Any} fnOrProperty
|
|
426
|
+
* @return {Any} fnOrProperty in Router
|
|
427
|
+
*
|
|
428
|
+
* @api public
|
|
429
|
+
*/
|
|
430
|
+
extend(key='', fnOrProperty) {
|
|
431
|
+
const keys = Object.keys(this);
|
|
432
|
+
if (keys.indexOf(key) > -1) {
|
|
433
|
+
const err = new TypeError(`Key ${ key } is already present in Router instance`);
|
|
434
|
+
throw err;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
this[key] = fnOrProperty;
|
|
438
|
+
|
|
439
|
+
return this[key];
|
|
440
|
+
}
|
|
441
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
// Utils.
|
|
9
|
+
const { typeOf } = require('../utils/types.util');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
module.exports = class NodesterRoute {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialize a new `NodesterRoute`.
|
|
16
|
+
*
|
|
17
|
+
* @param {Object|String} routeStringOrOpts
|
|
18
|
+
* - {String} method
|
|
19
|
+
* - {String} route
|
|
20
|
+
* - {Array} parts
|
|
21
|
+
* - {Object} params
|
|
22
|
+
*
|
|
23
|
+
* @api public
|
|
24
|
+
*/
|
|
25
|
+
constructor(routeStringOrOpts={}) {
|
|
26
|
+
if (typeOf(routeStringOrOpts) === 'Object') {
|
|
27
|
+
this.method = opts.method ?? undefined;
|
|
28
|
+
this.route = opts.route ?? null;
|
|
29
|
+
this.parts = opts.parts ?? [];
|
|
30
|
+
this.params = opts.params ?? {};
|
|
31
|
+
}
|
|
32
|
+
else if (typeOf(routeStringOrOpts) === 'string'){
|
|
33
|
+
this.method = undefined;
|
|
34
|
+
this.route = null;
|
|
35
|
+
this.parts = [];
|
|
36
|
+
this.params = {};
|
|
37
|
+
|
|
38
|
+
if (routeStringOrOpts.length === 0) {
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parse:
|
|
43
|
+
const parts = routeStringOrOpts.split(' ');
|
|
44
|
+
const cleared = parts.filter(p => p.length > 0);
|
|
45
|
+
|
|
46
|
+
// Set method:
|
|
47
|
+
if (cleared[0].indexOf('/') === -1) {
|
|
48
|
+
const method = cleared.shift().toUpperCase();
|
|
49
|
+
this.method = method;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Build route one again and set it:
|
|
53
|
+
const clearRoute = cleared.join('');
|
|
54
|
+
this.route = clearRoute;
|
|
55
|
+
|
|
56
|
+
// Parse path parts:
|
|
57
|
+
const pathParts = clearRoute.split('/')
|
|
58
|
+
.filter(p => p.length > 0);
|
|
59
|
+
this.parts = pathParts;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {String} routeToTest
|
|
66
|
+
*
|
|
67
|
+
* @return {Boolean}
|
|
68
|
+
*
|
|
69
|
+
* @alias matches
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
matches(routeToTest='') {
|
|
73
|
+
const {
|
|
74
|
+
route,
|
|
75
|
+
parts,
|
|
76
|
+
params
|
|
77
|
+
} = this;
|
|
78
|
+
|
|
79
|
+
if (routeToTest === route) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Break route & test:
|
|
84
|
+
const testParts = routeToTest.split('/')
|
|
85
|
+
.filter(p => p.length > 0);
|
|
86
|
+
let matched = true;
|
|
87
|
+
if (testParts.length === 0) {
|
|
88
|
+
matched = route === '/';
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
for (let i=0; i < testParts.length; i++) {
|
|
92
|
+
const part = parts[i];
|
|
93
|
+
const testPart = testParts[i];
|
|
94
|
+
|
|
95
|
+
// If route is shorter:
|
|
96
|
+
if (!part) {
|
|
97
|
+
matched = false;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// If part is wild:
|
|
102
|
+
if (part === '*') {
|
|
103
|
+
matched = true;
|
|
104
|
+
this.params.pathParts = testParts.slice(i);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// If part is param:
|
|
109
|
+
if (part[0] === ':') {
|
|
110
|
+
this.params[part.split(':')[1]] = testPart;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// If not matched:
|
|
115
|
+
if (part !== testPart) {
|
|
116
|
+
matched = false;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return matched;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
// Utils:
|
|
9
|
+
const { typeOf } = require('../utils/types.util');
|
|
10
|
+
const { parseRouteHandler } = require('./handlers.util');
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
wrapRouteHandler: _wrapRouteHandler
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wraps route handler.
|
|
20
|
+
*
|
|
21
|
+
* @param {Route} routeInstance
|
|
22
|
+
* @param {Function} handler
|
|
23
|
+
*
|
|
24
|
+
* @alias wrapRouteHandler
|
|
25
|
+
* @api public
|
|
26
|
+
*/
|
|
27
|
+
function _wrapRouteHandler(routeInstance, handler) {
|
|
28
|
+
const handlerType = typeOf(handler);
|
|
29
|
+
|
|
30
|
+
const wrapped = async (req, res, next) => {
|
|
31
|
+
// Get route without SearchParams.
|
|
32
|
+
const route = req.url.split('?')[0];
|
|
33
|
+
|
|
34
|
+
// If route not matched:
|
|
35
|
+
const methodMatched = routeInstance.method === req.method;
|
|
36
|
+
const routeMatched = routeInstance.matches(route);
|
|
37
|
+
if (!methodMatched || !routeMatched) {
|
|
38
|
+
return next();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Populate params.
|
|
42
|
+
req.params = routeInstance.params;
|
|
43
|
+
|
|
44
|
+
if (handlerType === 'function') {
|
|
45
|
+
await handler.call(this, req, res, next);
|
|
46
|
+
}
|
|
47
|
+
// If handler is an Object:
|
|
48
|
+
else {
|
|
49
|
+
const parsedHandler = parseRouteHandler(handler);
|
|
50
|
+
|
|
51
|
+
let providedAction = null;
|
|
52
|
+
|
|
53
|
+
// If Controller:
|
|
54
|
+
if (parsedHandler.controllerName !== undefined) {
|
|
55
|
+
// Get method (action) from Controller:
|
|
56
|
+
const controller = this._controllers.get(parsedHandler.controllerName);
|
|
57
|
+
const controllerAction = controller[parsedHandler.actionName];
|
|
58
|
+
|
|
59
|
+
providedAction = controllerAction;
|
|
60
|
+
}
|
|
61
|
+
// If Controller\
|
|
62
|
+
|
|
63
|
+
// If Provider:
|
|
64
|
+
else {
|
|
65
|
+
// Get method (action) from Provider:
|
|
66
|
+
const provider = this._providers.get(parsedHandler.providerName);
|
|
67
|
+
const providerAction = provider[parsedHandler.actionName];
|
|
68
|
+
|
|
69
|
+
providedAction = providerAction;
|
|
70
|
+
}
|
|
71
|
+
// If Provider\
|
|
72
|
+
|
|
73
|
+
// If User set any handler before,
|
|
74
|
+
// call it first:
|
|
75
|
+
if (typeOf(parsedHandler.before) === 'function') {
|
|
76
|
+
// Expose nquery first.
|
|
77
|
+
await parsedHandler.before(req.nquery, req, res);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await providedAction(req, res);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return wrapped;
|
|
85
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
parseProviderFileNames: _parseProviderFileNames
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function _parseProviderFileNames(fileNames, availableFileExtensions, term='controller') {
|
|
7
|
+
const resultNames = [];
|
|
8
|
+
|
|
9
|
+
for (const fileName of fileNames) {
|
|
10
|
+
|
|
11
|
+
const nameParts = fileName.split('.');
|
|
12
|
+
const extension = nameParts.pop();
|
|
13
|
+
if (availableFileExtensions.indexOf(extension) === -1) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// If the name format is <model>.<term>,
|
|
18
|
+
// but second part is not "term":
|
|
19
|
+
if (nameParts.length > 1 && nameParts[1] !== term)
|
|
20
|
+
continue;
|
|
21
|
+
|
|
22
|
+
const result = {
|
|
23
|
+
fileName: fileName,
|
|
24
|
+
[`${ term }Name`]: nameParts[0]
|
|
25
|
+
}
|
|
26
|
+
resultNames.push(result);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return resultNames;
|
|
30
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const consl = require('../logger/console');
|
|
2
|
+
const debug = require('debug')('nodester:MiddlewareStack');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module.exports = class MarkersStack {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.markers = new Map();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Add the given middleware `fn` to the stack.
|
|
13
|
+
*
|
|
14
|
+
* @param {String} markerName
|
|
15
|
+
* @param {Function} fn
|
|
16
|
+
* @return {Integer} index of new middleware
|
|
17
|
+
*
|
|
18
|
+
* @api public
|
|
19
|
+
*/
|
|
20
|
+
add(markerName='', fn) {
|
|
21
|
+
return this.markers.set(markerName, fn);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get middleware function by marker name.
|
|
27
|
+
*
|
|
28
|
+
* @param {String} markerName
|
|
29
|
+
*
|
|
30
|
+
* @api public
|
|
31
|
+
*/
|
|
32
|
+
get(markerName='') {
|
|
33
|
+
return this.markers.get(markerName);
|
|
34
|
+
}
|
|
35
|
+
}
|