nodester 0.1.5 → 0.2.0

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.
Files changed (58) hide show
  1. package/Readme.md +16 -55
  2. package/lib/application/index.js +174 -63
  3. package/lib/body/extract.js +15 -4
  4. package/lib/constants/Bounds.js +15 -0
  5. package/lib/constants/Clauses.js +13 -0
  6. package/lib/constants/ResponseFormats.js +2 -2
  7. package/lib/controllers/methods/index.js +7 -0
  8. package/lib/controllers/mixins/index.js +36 -36
  9. package/lib/database/connection.js +6 -0
  10. package/lib/database/migration.js +14 -4
  11. package/lib/facades/methods/index.js +10 -9
  12. package/lib/facades/mixins/index.js +67 -13
  13. package/lib/factories/responses/rest.js +25 -13
  14. package/lib/http/{request.js → request/index.js} +53 -75
  15. package/lib/http/request/utils.js +27 -0
  16. package/lib/http/response/headers.js +138 -0
  17. package/lib/http/response/index.js +248 -0
  18. package/lib/http/response/utils.js +38 -0
  19. package/lib/middlewares/SearchParams/index.js +25 -0
  20. package/lib/middlewares/cookies/index.js +44 -0
  21. package/lib/middlewares/etag/index.js +32 -15
  22. package/lib/middlewares/formidable/index.js +30 -25
  23. package/lib/middlewares/ql/sequelize/index.js +11 -2
  24. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +2 -1
  25. package/lib/middlewares/render/index.js +62 -0
  26. package/lib/models/associate.js +25 -1
  27. package/lib/models/define.js +19 -15
  28. package/lib/models/mixins.js +7 -0
  29. package/lib/query/traverse.js +96 -63
  30. package/lib/router/handlers.util.js +1 -0
  31. package/lib/router/index.js +193 -98
  32. package/lib/router/markers.js +7 -0
  33. package/lib/router/route.js +5 -0
  34. package/lib/router/routes.util.js +12 -14
  35. package/lib/router/utils.js +7 -0
  36. package/lib/stacks/MarkersStack.js +41 -3
  37. package/lib/stacks/MiddlewaresStack.js +200 -0
  38. package/lib/structures/Enum.js +46 -0
  39. package/lib/structures/Filter.js +156 -0
  40. package/lib/structures/Params.js +55 -0
  41. package/lib/tools/sql.tool.js +7 -0
  42. package/lib/utils/objects.util.js +31 -24
  43. package/lib/utils/sanitizations.util.js +10 -4
  44. package/lib/validators/arguments.js +68 -0
  45. package/lib/validators/dates.js +7 -0
  46. package/lib/validators/numbers.js +7 -0
  47. package/package.json +11 -8
  48. package/lib/database/utils.js +0 -19
  49. package/lib/enums/Enum.js +0 -16
  50. package/lib/filters/Filter.js +0 -109
  51. package/lib/http/response.js +0 -1074
  52. package/lib/http/utils.js +0 -254
  53. package/lib/params/Params.js +0 -37
  54. package/lib/policies/Role.js +0 -77
  55. package/lib/policies/RoleExtracting.js +0 -97
  56. package/lib/services/includes.service.js +0 -79
  57. package/lib/services/jwt.service.js +0 -147
  58. package/lib/stacks/MiddlewareStack.js +0 -159
package/Readme.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # nodester
2
2
  > A robust and flexible boilerplate framework that makes iterative development easy.
3
3
 
4
+ [![NPM version](https://img.shields.io/npm/v/nodester)](https://www.npmjs.com/package/nodester)
5
+ [![License](https://img.shields.io/npm/l/nodester)](https://www.npmjs.com/package/nodester)
6
+
7
+
8
+ ## Installation
9
+
10
+ Install with NPM
11
+
12
+ ```shell
13
+ npm install -S nodester
14
+ ```
15
+
16
+
4
17
  ## Table of Contents
5
18
 
6
19
  - [Usage](#usage)
@@ -24,6 +37,7 @@ app.listen(8080, function() {
24
37
  console.log('listening on port', app.port);
25
38
  });
26
39
  ```
40
+ [How to setup "db" ➡️](docs/App.md#with-database)
27
41
 
28
42
 
29
43
  ## Documentation
@@ -46,61 +60,8 @@ Supported drivers:
46
60
  - PostgreSQL
47
61
 
48
62
 
49
- ### Extending Application functionality
50
-
51
-
52
- #### Extending instance (safe way):
53
-
54
- ```js
55
- const serveStatic = require('serve-static');
56
-
57
- const nodester = require('nodester');
58
-
59
- const app = new nodester();
60
- app.extend('static', serveStatic);
61
- app.static(<path_to_static_directory/>);
62
- ```
63
-
64
- Short:
65
- ```js
66
- const serveStatic = require('serve-static');
67
-
68
- const nodester = require('nodester');
69
-
70
- const app = new nodester();
71
- app.extend('static', serveStatic)(<path_to_static_directory/>);
72
- ```
73
-
74
- Of course you might just do this:
75
- ```js
76
- const serveStatic = require('serve-static');
77
-
78
- const nodester = require('nodester');
79
-
80
- const app = new nodester();
81
- app.static = serveStatic;
82
- ````
83
- But you'll never know if you did override any of the app's properties or did not.
84
-
85
-
86
- #### Extending class:
87
-
88
- If you really want to override properties or use `nodester` as a boilerplate, you should extend default Application class:
89
-
90
- ```js
91
- const NodesterApp = require('nodester');
92
-
93
- class MyApp extends NodesterApp {
94
- constructor(opts) {
95
- super(opts)
96
- }
97
-
98
- // Override everything you want here...
99
- }
100
-
101
- // Don't forget to expose.
102
- module.exports = MyApp;
103
- ```
63
+ ### Application
64
+ [Application documentation ➡️](docs/App.md)
104
65
 
105
66
 
106
67
  ## Philosophy
@@ -6,7 +6,7 @@
6
6
  'use strict';
7
7
 
8
8
  const Emitter = require('events');
9
- const DefaultRouter = require('../router');
9
+ const NodesterRouter = require('../router');
10
10
 
11
11
  // Server:
12
12
  const http = require('http');
@@ -14,45 +14,70 @@ const request = require('../http/request');
14
14
  const response = require('../http/response');
15
15
 
16
16
  // Middlewares:
17
- const nodesterQL = require('../middlewares/ql/sequelize');
18
17
  const bodyParser = require('body-parser');
18
+ const cookies = require('nodester/middlewares/cookies');
19
+ const nodesterQL = require('nodester/ql/sequelize');
20
+
21
+ // DB setup.
22
+ const { associateModels } = require('nodester/models/associate');
19
23
 
20
24
  // Utils:
25
+ const Params = require('nodester/params');
21
26
  const {
22
27
  typeOf,
23
28
  isConstructor
24
29
  } = require('../utils/types.util');
25
- const {
26
- associateModels
27
- } = require('../database/utils');
28
-
29
30
  const { merge } = require('../utils/objects.util');
31
+
32
+ // Arguments validator.
33
+ const { ensure } = require('../validators/arguments');
34
+
35
+ // Console:
30
36
  const consl = require('nodester/loggers/console');
31
37
  const debug = require('debug')('nodester:application');
32
38
 
33
39
 
34
- module.exports = class Application extends Emitter {
40
+ module.exports = class NodesterApplication extends Emitter {
35
41
 
36
42
  /**
37
- * Initialize a new `Application`.
43
+ * Initialize new `NodesterApplication`.
44
+ *
45
+ * @param {Object} [options]
46
+ * @param {String|Int} options.port
47
+ * @param {String} options.title
48
+ * @param {Boolean} options.finalhandlerEnabled
49
+ * @param {Object} options.middlewares
38
50
  *
39
51
  * @api public
40
52
  */
41
- constructor(opts={}) {
53
+ constructor(options={}) {
42
54
  super();
43
55
 
56
+ ensure(options, 'object', 'options');
57
+
58
+ // Unwrap options:
59
+ const {
60
+ port,
61
+ title,
62
+ finalhandlerEnabled,
63
+ middlewares
64
+ } = Params((options ?? {}), {
65
+ port: 8080,
66
+ title: null,
67
+ finalhandlerEnabled: true,
68
+ middlewares: {}
69
+ });
70
+
71
+
44
72
  // Fallback port.
45
- this.port = opts?.port ?? 8080;
73
+ this.port = port;
46
74
 
47
- // Reference to router.
48
- this._router = new DefaultRouter({ finalhandlerEnabled: true });
75
+ // Router acts as a MiddlewareStack for the application.
76
+ this._router = new NodesterRouter({ finalhandlerEnabled });
49
77
 
50
78
  // Reference to the database connection.
51
79
  this._database = null;
52
80
 
53
- // Reference to the Query parser.
54
- this._queryParser = nodesterQL;
55
-
56
81
  // Reference to the http(s) server,
57
82
  this.server = null;
58
83
 
@@ -61,16 +86,56 @@ module.exports = class Application extends Emitter {
61
86
  };
62
87
 
63
88
  // Default middlewares:
64
- const _withoutMiddlewares = opts?.middlewares?.without ?? [];
89
+ const _withoutMiddlewares = middlewares?.without ?? [];
90
+
91
+ // !WARNING: Order of middlewares is important:
92
+
93
+ if (_withoutMiddlewares.indexOf('nodester-ql') === -1) {
94
+ this.use(nodesterQL());
95
+ }
65
96
 
66
97
  if (_withoutMiddlewares.indexOf('body-parser') === -1) {
67
98
  this.use(bodyParser.json());
68
99
  }
69
100
 
101
+ if (_withoutMiddlewares.indexOf('cookies') === -1) {
102
+ this.use(cookies());
103
+ }
104
+
105
+ // User can title their application:
106
+ if (!!title) {
107
+ // Rename CLI process.
108
+ process.title = title;
109
+ }
110
+
70
111
  // Indicatorors.
71
- this.isListening = false;
112
+ this._isListening = false;
113
+ }
114
+
115
+ // Getters:
116
+
117
+ /**
118
+ * @return {MiddlewaresStack}
119
+ *
120
+ * @api public
121
+ */
122
+ get middlewaresStack() {
123
+ return this._router.middlewaresStack;
124
+ }
125
+
126
+ /**
127
+ * Indicates whether user can add more middlewares or not.
128
+ *
129
+ * @return {Boolean} isLocked
130
+ *
131
+ * @api public
132
+ */
133
+ get isLocked() {
134
+ return this._router.isLocked;
72
135
  }
73
136
 
137
+ // Getters\
138
+
74
139
  /*
75
140
  * Expose the prototype that will get set on requests.
76
141
  *
@@ -98,7 +163,7 @@ module.exports = class Application extends Emitter {
98
163
  /*
99
164
  * Sets (or overrides):
100
165
  * - (database) main database of the application and tries to make connection
101
- * - (router) default Router
166
+ * - (router) default NodesterRouter
102
167
  *
103
168
  * @api public
104
169
  */
@@ -114,34 +179,42 @@ module.exports = class Application extends Emitter {
114
179
  * Sets (or overrides) main database of the application and tries to make connection.
115
180
  *
116
181
  * @param {Sequilize} sequilizeConnection
117
- * @param {Boolean} crashOnError
182
+ * @param {Object} [options]
183
+ * @param {Boolean} options.associateModels
184
+ * @param {Boolean} options.crashOnError
185
+ *
118
186
  * @return {sequilizeConnection.authenticate}
119
187
  *
120
- * @public
188
+ * @api public
121
189
  */
122
- async setDatabase(sequilizeConnection, crashOnError=true) {
190
+ async setDatabase(sequilizeConnection, options={}) {
123
191
  try {
124
192
  if (!sequilizeConnection) {
125
- const err = new TypeError('Connection to database (Sequilize) can not be null or undefined.');
193
+ const err = new TypeError('Connection to a database (Sequilize) can not be null or undefined.');
126
194
  throw err;
127
195
  }
128
196
 
129
- if (this.isListening === true) {
130
- const err = new Error(`Can't set database after application start.`);
197
+ if (this._isListening === true) {
198
+ const err = new Error(`Can't set database after an application start.`);
131
199
  throw err;
132
200
  }
133
201
 
134
202
  // Test connection.
135
203
  const result = await sequilizeConnection.authenticate();
136
- // Associate models.
137
- await associateModels(sequilizeConnection.models);
204
+
205
+ if (options?.associateModels === true) {
206
+ // Associate models between each other.
207
+ await associateModels(sequilizeConnection);
208
+ }
209
+
138
210
  // Set database.
139
211
  this._database = sequilizeConnection;
140
212
 
141
213
  return Promise.resolve(result);
142
214
  }
143
215
  catch(error) {
144
- if (crashOnError === true) {
216
+ if (options?.crashOnError === true) {
217
+ Error.captureStackTrace(error, this.setDatabase);
145
218
  throw error;
146
219
  }
147
220
  else {
@@ -156,9 +229,9 @@ module.exports = class Application extends Emitter {
156
229
  /*
157
230
  * Returns main database of the application.
158
231
  *
159
- * @return {SequilizeConnection}
232
+ * @return {SequilizeConnection} database
160
233
  *
161
- * @public
234
+ * @api public
162
235
  */
163
236
  get database() {
164
237
  return this._database;
@@ -168,7 +241,9 @@ module.exports = class Application extends Emitter {
168
241
  /**
169
242
  * Overrides default Router.
170
243
  *
171
- * @public
244
+ * @param {NodesterRouter} newRouter
245
+ *
246
+ * @api public
172
247
  */
173
248
  setRouter(newRouter) {
174
249
  if (isConstructor(newRouter)) {
@@ -185,7 +260,7 @@ module.exports = class Application extends Emitter {
185
260
  *
186
261
  * @return {NodesterRouter}
187
262
  *
188
- * @public
263
+ * @api public
189
264
  */
190
265
  get router() {
191
266
  return this._router;
@@ -216,7 +291,15 @@ module.exports = class Application extends Emitter {
216
291
  * @api public
217
292
  */
218
293
  use(fnOrRouter) {
219
- return this._router.use(fnOrRouter);
294
+ try {
295
+ ensure(fnOrRouter, 'object|function,required', 'fnOrRouter');
296
+
297
+ return this._router.use(fnOrRouter);
298
+ }
299
+ catch(error) {
300
+ Error.captureStackTrace(error, this.use);
301
+ throw error;
302
+ }
220
303
  }
221
304
 
222
305
 
@@ -227,7 +310,15 @@ module.exports = class Application extends Emitter {
227
310
  * @api public
228
311
  */
229
312
  only(markerName='') {
230
- return this._router.only(markerName);
313
+ try {
314
+ ensure(markerName, 'string,required', 'markerName');
315
+
316
+ return this._router.only(markerName);
317
+ }
318
+ catch(error) {
319
+ Error.captureStackTrace(error, this.only);
320
+ throw error;
321
+ }
231
322
  }
232
323
 
233
324
 
@@ -237,13 +328,16 @@ module.exports = class Application extends Emitter {
237
328
  * @api public
238
329
  */
239
330
  beforeStart(fn) {
240
- if (typeOf(fn) !== 'function') {
241
- const err = new TypeError('"fn" argument must be a function');
242
- throw err;
243
- }
244
- this._hooks.beforeStart = fn;
331
+ try {
332
+ ensure(fn, 'function,required', 'fn');
245
333
 
246
- return this._hooks.beforeStart;
334
+ this._hooks.beforeStart = fn;
335
+ return this._hooks.beforeStart;
336
+ }
337
+ catch(error) {
338
+ Error.captureStackTrace(error, this.beforeStart);
339
+ throw error;
340
+ }
247
341
  }
248
342
 
249
343
 
@@ -260,19 +354,25 @@ module.exports = class Application extends Emitter {
260
354
  await this._hooks.beforeStart.call(this);
261
355
  }
262
356
  catch(error) {
263
- console.error('Application did not start due to error.');
357
+ console.error('NodesterApplication did not start due to error.');
264
358
  consl.error(error);
359
+
360
+ Error.captureStackTrace(error, this.start);
265
361
  return Promise.reject(error);
266
362
  }
267
363
 
268
- // Add query parser.
269
- this._router.add.middleware(this._queryParser, 0);
270
364
 
271
- // Prepare router for processing.
272
- this._router.lock();
365
+ try {
366
+ // Prepare router for processing.
367
+ this._router.lock();
273
368
 
274
- const handler = this.handle.bind(this);
275
- return handler;
369
+ const handler = this.handle.bind(this);
370
+ return handler;
371
+ }
372
+ catch(error) {
373
+ Error.captureStackTrace(error, this.start);
374
+ return Promise.reject(error);
375
+ }
276
376
  }
277
377
 
278
378
 
@@ -281,26 +381,35 @@ module.exports = class Application extends Emitter {
281
381
  *
282
382
  * http.createServer(app.start()).listen(...)
283
383
  *
284
- * @param {Int} port
384
+ * @param {Integer|String} port
285
385
  * @param {Mixed} ...
386
+ *
286
387
  * @return {import('http').Server}
287
388
  *
288
389
  * @api public
289
390
  */
290
391
  async listen(port, ...args) {
291
- // Remember port:
292
- const _port = port ?? this.port;
293
- this.port = _port;
392
+ try {
393
+ ensure(port, 'number|string,required', 'port');
294
394
 
295
- debug(`listen on port ${ this.port }`);
395
+ // Remember port:
396
+ const _port = port ?? this.port;
397
+ this.port = _port;
296
398
 
297
- if (!this.server) {
298
- const handler = await this.start();
299
- this.server = http.createServer(handler);
300
- }
399
+ debug(`listen on port ${ this.port }`);
400
+
401
+ if (!this.server) {
402
+ const handler = await this.start();
403
+ this.server = http.createServer(handler);
404
+ }
301
405
 
302
- this.isListening = true;
303
- return this.server.listen(_port, ...args);
406
+ this._isListening = true;
407
+ return this.server.listen(_port, ...args);
408
+ }
409
+ catch(error) {
410
+ Error.captureStackTrace(error, this.listen);
411
+ throw error;
412
+ }
304
413
  }
305
414
 
306
415
 
@@ -324,15 +433,19 @@ module.exports = class Application extends Emitter {
324
433
 
325
434
  /**
326
435
  * Extends Application & makes sure, that "key" param is not present already.
436
+ *
327
437
  * @param {String} key
328
438
  * @param {Any} fnOrProperty
439
+ *
329
440
  * @return {Any} fnOrProperty in Application
330
441
  *
331
442
  * @api public
332
443
  */
333
444
  extend(key='', fnOrProperty) {
334
- const keys = Object.keys(this);
335
- if (keys.indexOf(key) > -1) {
445
+ ensure(key, 'string,required', 'key');
446
+ ensure(fnOrProperty, 'required', 'fnOrProperty');
447
+
448
+ if (typeof this[key] !== 'undefined') {
336
449
  const err = new TypeError(`Key ${ key } is already present in Application instance`);
337
450
  throw err;
338
451
  }
@@ -349,16 +462,14 @@ module.exports = class Application extends Emitter {
349
462
  * @api public
350
463
  */
351
464
  stop() {
352
- if (this.isListening !== true) {
465
+ if (this._isListening !== true) {
353
466
  console.warn('Nothing to stop. Server is not listening.');
354
467
  return;
355
468
  }
356
469
 
357
470
  this.server.close();
358
- this.isListening = false;
471
+ this._isListening = false;
359
472
 
360
473
  this._router.unlock();
361
- // Remove query parser.
362
- this._router.remove.middleware(0);
363
474
  }
364
475
  }
@@ -2,6 +2,7 @@
2
2
  * /nodester
3
3
  * MIT Licensed
4
4
  */
5
+
5
6
  'use strict';
6
7
 
7
8
  const httpCodes = require('nodester/http/codes');
@@ -11,6 +12,16 @@ const Sanitizations = require('nodester/utils/sanitizations');
11
12
 
12
13
  module.exports = extract;
13
14
 
15
+ /*
16
+ * Extracts data from the body, based on the rules in "filter".
17
+ *
18
+ * @param {Object} body
19
+ * @param {NodesterFilter} filter
20
+ * @param {SequilizeModel} model
21
+ *
22
+ * @api public
23
+ * @return {Object} filteredBody
24
+ */
14
25
  function extract(body, filter=null, model) {
15
26
 
16
27
  const sequelize = model.sequelize;
@@ -23,7 +34,7 @@ function extract(body, filter=null, model) {
23
34
 
24
35
 
25
36
  // Result object.
26
- const newBody = {};
37
+ const filteredBody = {};
27
38
 
28
39
  for (const [key, value] of bodyEntries) {
29
40
  const isInclude = availableIncludes.indexOf(key) > -1;
@@ -48,7 +59,7 @@ function extract(body, filter=null, model) {
48
59
  }
49
60
  const sanitized = Sanitizations[typeName](value, sanitizationOptions);
50
61
 
51
- newBody[key] = sanitized;
62
+ filteredBody[key] = sanitized;
52
63
 
53
64
  continue;
54
65
  }
@@ -64,7 +75,7 @@ function extract(body, filter=null, model) {
64
75
 
65
76
  const association = model.associations[key];
66
77
 
67
- newBody[key] = extract(value[0], filter.includes[key], association.target);
78
+ filteredBody[key] = extract(value[0], filter.includes[key], association.target);
68
79
 
69
80
  continue;
70
81
  }
@@ -74,5 +85,5 @@ function extract(body, filter=null, model) {
74
85
  throw err;
75
86
  }
76
87
 
77
- return newBody;
88
+ return filteredBody;
78
89
  }
@@ -0,0 +1,15 @@
1
+ const Enum = require('nodester/enum');
2
+
3
+ const limit = {
4
+ min: 1,
5
+ max: 3,
6
+ }
7
+
8
+ const skip = {
9
+ min: 0
10
+ }
11
+
12
+ module.exports = new Enum({
13
+ limit,
14
+ skip
15
+ });
@@ -0,0 +1,13 @@
1
+ const Enum = require('nodester/enum');
2
+
3
+ const LIMIT = 'limit';
4
+ const SKIP = 'skip';
5
+ const ORDER = 'order';
6
+ const ORDER_BY = 'order_by';
7
+
8
+ module.exports = new Enum({
9
+ LIMIT,
10
+ SKIP,
11
+ ORDER,
12
+ ORDER_BY,
13
+ });
@@ -1,7 +1,7 @@
1
- const ConstantsEnum = require('nodester/constants/ConstantsEnum');
1
+ const Enum = require('nodester/enum');
2
2
 
3
3
 
4
- module.exports = new ConstantsEnum({
4
+ module.exports = new Enum({
5
5
  JSON: 'JSON',
6
6
  XML: 'XML',
7
7
  });
@@ -1,3 +1,10 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
 
2
9
  module.exports = {
3
10
  getOne: _getOne,