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.
Files changed (69) hide show
  1. package/Readme.md +33 -39
  2. package/lib/application/index.js +110 -38
  3. package/lib/constants/Operations.js +23 -0
  4. package/lib/controllers/methods/index.js +194 -0
  5. package/lib/controllers/mixins/index.js +222 -0
  6. package/lib/database/connection.js +34 -0
  7. package/lib/database/migration.js +42 -0
  8. package/lib/database/utils.js +19 -0
  9. package/lib/enums/Enum.js +16 -0
  10. package/lib/facades/methods/index.js +173 -0
  11. package/lib/facades/mixins/index.js +111 -0
  12. package/lib/factories/errors/CustomError.js +7 -5
  13. package/lib/factories/responses/html.js +7 -2
  14. package/lib/factories/responses/rest.js +110 -0
  15. package/lib/http/codes/index.js +157 -0
  16. package/lib/{application/http → http}/request.js +6 -30
  17. package/lib/{application/http → http}/response.js +20 -53
  18. package/lib/middlewares/etag/index.js +62 -0
  19. package/lib/middlewares/ql/sequelize/index.js +34 -0
  20. package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +121 -0
  21. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +456 -0
  22. package/lib/models/associate.js +17 -0
  23. package/lib/models/define.js +56 -14
  24. package/lib/models/mixins.js +100 -78
  25. package/lib/params/Params.js +37 -0
  26. package/lib/queries/Colander.js +84 -0
  27. package/lib/queries/NodesterQueryParams.js +139 -0
  28. package/lib/queries/traverse.js +311 -0
  29. package/lib/router/handlers.util.js +81 -0
  30. package/lib/router/index.js +441 -0
  31. package/lib/router/route.js +124 -0
  32. package/lib/router/routes.util.js +85 -0
  33. package/lib/router/utils.js +30 -0
  34. package/lib/stacks/MarkersStack.js +35 -0
  35. package/lib/{application → stacks}/MiddlewareStack.js +47 -13
  36. package/lib/utils/path.util.js +3 -1
  37. package/lib/utils/types.util.js +51 -1
  38. package/lib/validators/dates.js +25 -0
  39. package/lib/validators/numbers.js +14 -0
  40. package/package.json +31 -4
  41. package/tests/index.test.js +7 -2
  42. package/tests/nql.test.js +277 -0
  43. package/docs/App.md +0 -13
  44. package/docs/Queries.md +0 -61
  45. package/docs/Readme.md +0 -2
  46. package/docs/Routing.md +0 -34
  47. package/examples/goal/index.js +0 -23
  48. package/examples/rest/index.js +0 -25
  49. package/examples/rest/node_modules/.package-lock.json +0 -40
  50. package/examples/rest/package-lock.json +0 -72
  51. package/examples/rest/package.json +0 -14
  52. package/lib/constants/ConstantsEnum.js +0 -13
  53. package/lib/controllers/Controller.js +0 -474
  54. package/lib/controllers/JWTController.js +0 -240
  55. package/lib/controllers/ServiceController.js +0 -109
  56. package/lib/controllers/WebController.js +0 -75
  57. package/lib/facades/Facade.js +0 -388
  58. package/lib/facades/FacadeParams.js +0 -11
  59. package/lib/facades/ServiceFacade.js +0 -17
  60. package/lib/facades/jwt.facade.js +0 -273
  61. package/lib/factories/responses/api.js +0 -90
  62. package/lib/models/DisabledRefreshToken.js +0 -68
  63. package/lib/models/Extractor.js +0 -320
  64. package/lib/routers/Default/index.js +0 -143
  65. package/lib/routers/Default/layer.js +0 -50
  66. package/lib/routers/Main/index.js +0 -10
  67. package/lib/routers/Roles/index.js +0 -81
  68. package/lib/utils/params.util.js +0 -19
  69. /package/lib/{application/http → http}/utils.js +0 -0
package/Readme.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # nodester
2
- > A robust boilerplate framework that makes iterative development easy.
2
+ > A robust and flexible boilerplate framework that makes iterative development easy.
3
3
 
4
4
  ## Table of Contents
5
5
 
6
6
  - [Usage](#usage)
7
- - [Extending](#extending-application-functionality)
7
+ - [Documentation](#documentation)
8
+ - [Extending App](#extending-application-functionality)
8
9
  - [Philosophy](#philosophy)
9
10
  - [License](#license)
10
11
  - [Copyright](#copyright)
11
12
 
13
+
12
14
  ## Usage
13
15
 
14
16
  ```js
@@ -19,52 +21,43 @@ const app = new nodester();
19
21
  app.set.database(db);
20
22
 
21
23
  app.listen(8080, function() {
22
- console.log('listening on port', app.port);
24
+ console.log('listening on port', app.port);
23
25
  });
24
26
  ```
25
27
 
26
- ## Markers
27
-
28
- Marker is a functional condition that returns `true | false`, based on data in request.
29
28
 
29
+ ## Documentation
30
30
 
31
- ### Adding a Marker
32
31
 
33
- ```js
34
- app.add.marker('admin', (req) => req.role === 'admin');
35
- ```
32
+ ### Core concepts
33
+ [Go to core concepts documentaion](docs/CoreConcepts.md)
36
34
 
35
+ ### Queries & Querying - Nodester Query Language (NQR)
36
+ One of the main power points of nodester is it's query language. It's an extension of a REST API syntaxis for a broader integration with a database SQL. Read more about it in the documentation:
37
37
 
38
- ### Using a Marker
39
-
40
- ```js
41
- app.only('admin').route('get /payments', <handler>);
42
- app.only('/api').route('get /payments', <handler>);
43
- ```
44
-
38
+ [Go to NQR documentaion](docs/Queries.md)
45
39
 
46
- ## Router
47
40
 
48
- ```js
49
- app.route('get /books', function(req, res) { ... } );
50
- app.route('get /books', { controlledBy: 'BooksController.getMany' } );
51
- app.route('get /books/:id', { controlledBy: 'BooksController.getOne' } );
52
- ```
41
+ ### Database
42
+ Nodester is built upon a powerful [Sequelize](https://sequelize.org/).
43
+ Supported drivers:
44
+ - MySQL
45
+ - PostgreSQL
53
46
 
54
47
 
55
- ## Extending Application functionality
48
+ ### Extending Application functionality
56
49
 
57
50
 
58
- ### Extending instance (safe way):
51
+ #### Extending instance (safe way):
59
52
 
60
53
  ```js
61
54
  const serveStatic = require('serve-static');
62
55
 
63
56
  const nodester = require('nodester');
64
57
 
65
- const app = nodester();
58
+ const app = new nodester();
66
59
  app.extend('static', serveStatic);
67
- app.static(<path_to_static_directory>);
60
+ app.static(<path_to_static_directory/>);
68
61
  ```
69
62
 
70
63
  Short:
@@ -73,8 +66,8 @@ const serveStatic = require('serve-static');
73
66
 
74
67
  const nodester = require('nodester');
75
68
 
76
- const app = nodester();
77
- app.extend('static', serveStatic)(<path_to_static_directory>);
69
+ const app = new nodester();
70
+ app.extend('static', serveStatic)(<path_to_static_directory/>);
78
71
  ```
79
72
 
80
73
  Of course you might just do this:
@@ -83,38 +76,39 @@ const serveStatic = require('serve-static');
83
76
 
84
77
  const nodester = require('nodester');
85
78
 
86
- const app = nodester();
79
+ const app = new nodester();
87
80
  app.static = serveStatic;
88
81
  ````
89
82
  But you'll never know if you did override any of the app's properties or did not.
90
83
 
91
84
 
92
- ### Extending class:
85
+ #### Extending class:
93
86
 
94
- If you really want to override properties or use `nodester` as a boilerplate, you should better extend default Application class:
87
+ If you really want to override properties or use `nodester` as a boilerplate, you should extend default Application class:
95
88
 
96
89
  ```js
97
- const DefaultApplication = require('nodester');
90
+ const NodesterApp = require('nodester');
98
91
 
99
- class MyApp extends DefaultApplication {
100
- constructor(opts) {
101
- super(opts)
102
- }
92
+ class MyApp extends NodesterApp {
93
+ constructor(opts) {
94
+ super(opts)
95
+ }
103
96
 
104
- // Override everything you want here...
97
+ // Override everything you want here...
105
98
  }
106
99
 
107
100
  // Don't forget to expose.
108
101
  module.exports = MyApp;
109
102
  ```
110
103
 
104
+
111
105
  ## Philosophy
112
106
 
113
107
  The Philosophy of `nodester` is to provide a developer with a tool that can build an app (or feature) in hours and scale it with ease for years.
114
108
 
115
109
  ### Goal
116
110
 
117
- The goal of `nodester` is to be a robust boilerplate that makes iterative development easy.
111
+ The goal of `nodester` is to be a robust and flexible framework that makes development in iteratations easy, and further scale possible.
118
112
 
119
113
 
120
114
  ## LICENSE
@@ -6,13 +6,26 @@
6
6
  'use strict';
7
7
 
8
8
  const Emitter = require('events');
9
- const MiddlewareStack = require('./MiddlewareStack');
9
+ const DefaultRouter = require('../router');
10
+
10
11
  // Server:
11
12
  const http = require('http');
12
- const request = require('./http/request');
13
- const response = require('./http/response');
13
+ const request = require('../http/request');
14
+ const response = require('../http/response');
15
+
16
+ // Middlewares:
17
+ const nodesterQL = require('../middlewares/ql/sequelize');
18
+ const bodyParser = require('body-parser');
19
+
14
20
  // Utils:
15
- const { typeOf } = require('../utils/types.util');
21
+ const {
22
+ typeOf,
23
+ isConstructor
24
+ } = require('../utils/types.util');
25
+ const {
26
+ associateModels
27
+ } = require('../database/utils');
28
+
16
29
  const { merge } = require('../utils/objects.util');
17
30
  const consl = require('../logger/console');
18
31
  const debug = require('debug')('nodester:application');
@@ -31,11 +44,14 @@ module.exports = class Application extends Emitter {
31
44
  // Fallback port.
32
45
  this.port = opts?.port ?? 8080;
33
46
 
34
- // Reference to middlewares stack.
35
- this._middlewares = new MiddlewareStack();
47
+ // Reference to router.
48
+ this._router = new DefaultRouter({ finalhandlerEnabled: true });
36
49
 
37
50
  // Reference to the database connection.
38
- this.database = null;
51
+ this._database = null;
52
+
53
+ // Reference to the Query parser.
54
+ this._queryParser = nodesterQL;
39
55
 
40
56
  // Reference to the http(s) server,
41
57
  this.server = null;
@@ -44,6 +60,13 @@ module.exports = class Application extends Emitter {
44
60
  beforeStart: ()=>{}
45
61
  };
46
62
 
63
+ // Default middlewares:
64
+ const _withoutMiddlewares = opts?.middlewares?.without ?? [];
65
+
66
+ if (_withoutMiddlewares.indexOf('body-parser') === -1) {
67
+ this.use(bodyParser.json());
68
+ }
69
+
47
70
  // Indicatorors.
48
71
  this.isListening = false;
49
72
  }
@@ -55,7 +78,7 @@ module.exports = class Application extends Emitter {
55
78
  */
56
79
  get request() {
57
80
  return Object.create(request, {
58
- app: { configurable: true, enumerable: true, writable: true, value: app }
81
+ app: { configurable: true, enumerable: true, writable: true, value: this }
59
82
  })
60
83
  }
61
84
 
@@ -67,7 +90,7 @@ module.exports = class Application extends Emitter {
67
90
  */
68
91
  get response() {
69
92
  return Object.create(response, {
70
- app: { configurable: true, enumerable: true, writable: true, value: app }
93
+ app: { configurable: true, enumerable: true, writable: true, value: this }
71
94
  })
72
95
  }
73
96
 
@@ -89,23 +112,33 @@ module.exports = class Application extends Emitter {
89
112
 
90
113
  /**
91
114
  * Sets (or overrides) main database of the application and tries to make connection.
115
+ *
92
116
  * @param {Sequilize} sequilizeConnection
93
117
  * @param {Boolean} crashOnError
94
118
  * @return {sequilizeConnection.authenticate}
95
119
  *
96
120
  * @public
97
121
  */
98
- setDatabase(sequilizeConnection, crashOnError=true) {
122
+ async setDatabase(sequilizeConnection, crashOnError=true) {
99
123
  try {
100
124
  if (!sequilizeConnection) {
101
- const err = new Error('Connection to database (Sequilize) can not be null or undefined.');
125
+ const err = new TypeError('Connection to database (Sequilize) can not be null or undefined.');
102
126
  throw err;
103
127
  }
104
128
 
105
- const result = sequilizeConnection.authenticate();
106
- this.database = sequilizeConnection;
129
+ if (this.isListening === true) {
130
+ const err = new Error(`Can't set database after application start.`);
131
+ throw err;
132
+ }
107
133
 
108
- return result;
134
+ // Test connection.
135
+ const result = await sequilizeConnection.authenticate();
136
+ // Associate models.
137
+ await associateModels(sequilizeConnection.models);
138
+ // Set database.
139
+ this._database = sequilizeConnection;
140
+
141
+ return Promise.resolve(result);
109
142
  }
110
143
  catch(error) {
111
144
  if (crashOnError === true) {
@@ -114,17 +147,48 @@ module.exports = class Application extends Emitter {
114
147
  else {
115
148
  consl.error(error);
116
149
  }
150
+
151
+ return Promise.reject(error);
117
152
  }
118
153
  }
119
154
 
120
155
 
156
+ /*
157
+ * Returns main database of the application.
158
+ *
159
+ * @return {SequilizeConnection}
160
+ *
161
+ * @public
162
+ */
163
+ get database() {
164
+ return this._database;
165
+ }
166
+
167
+
121
168
  /**
122
169
  * Overrides default Router.
123
170
  *
124
171
  * @public
125
172
  */
126
173
  setRouter(newRouter) {
127
- this.router = newRouter.init(this);
174
+ if (isConstructor(newRouter)) {
175
+ this._router = new newRouter();
176
+ }
177
+ else {
178
+ this._router = newRouter;
179
+ }
180
+ }
181
+
182
+
183
+ /*
184
+ * Returns NodesterRouter object.
185
+ *
186
+ * @return {NodesterRouter}
187
+ *
188
+ * @public
189
+ */
190
+ get router() {
191
+ return this._router;
128
192
  }
129
193
 
130
194
 
@@ -136,40 +200,39 @@ module.exports = class Application extends Emitter {
136
200
  */
137
201
  get add() {
138
202
  return {
139
- middleware: this.addMiddleware.bind(this)
203
+ controller: this._router.add.controller,
204
+ middleware: this._router.add.middleware,
205
+ marker: this._router.add.marker,
206
+ route: this._router.add.route,
140
207
  }
141
208
  }
142
209
 
143
210
 
144
211
  /*
145
- * Adds new middleware to the stack.
212
+ * Proxy to .add.middleware()
213
+ *
214
+ * @param {Function} fn
146
215
  *
147
216
  * @api public
148
217
  */
149
- addMiddleware(fn) {
150
- if (this._middlewares.isLocked === true) {
151
- const err = new Error(`Can't add more middlewares after application has been started.`);
152
- throw err;
153
- }
154
-
155
- this._middlewares.add(fn);
218
+ use(fn) {
219
+ return this._router.add.middleware(fn);
156
220
  }
157
221
 
158
222
 
159
223
  /*
160
- * Proxy to .add().
161
224
  *
162
- * @param {Function} fn
225
+ * @param {String} markerName
163
226
  *
164
227
  * @api public
165
228
  */
166
- use(fn) {
167
- return this.add(fn);
229
+ only(markerName='') {
230
+ return this._router.only(markerName);
168
231
  }
169
232
 
170
233
 
171
234
  /**
172
- * Adds to hooks stack (beforeStart).
235
+ * Sets beforeStart hook.
173
236
  *
174
237
  * @api public
175
238
  */
@@ -192,20 +255,24 @@ module.exports = class Application extends Emitter {
192
255
  *
193
256
  * @api public
194
257
  */
195
- start() {
258
+ async start() {
196
259
  try {
197
- this._hooks.beforeStart.call(this);
260
+ await this._hooks.beforeStart.call(this);
198
261
  }
199
262
  catch(error) {
200
263
  console.error('Application did not start due to error.');
201
264
  consl.error(error);
202
- return;
265
+ return Promise.reject(error);
203
266
  }
204
267
 
205
- // Lock middlewares stack.
206
- this._middlewares.lock();
268
+ // Add query parser.
269
+ this._router.add.middleware(this._queryParser, 0);
207
270
 
208
- return this.handle.bind(this);
271
+ // Prepare router for processing.
272
+ this._router.lock();
273
+
274
+ const handler = this.handle.bind(this);
275
+ return handler;
209
276
  }
210
277
 
211
278
 
@@ -220,7 +287,7 @@ module.exports = class Application extends Emitter {
220
287
  *
221
288
  * @api public
222
289
  */
223
- listen(port, ...args) {
290
+ async listen(port, ...args) {
224
291
  // Remember port:
225
292
  const _port = port ?? this.port;
226
293
  this.port = _port;
@@ -228,7 +295,8 @@ module.exports = class Application extends Emitter {
228
295
  debug(`listen on port ${ this.port }`);
229
296
 
230
297
  if (!this.server) {
231
- this.server = http.createServer(this.start());
298
+ const handler = await this.start();
299
+ this.server = http.createServer(handler);
232
300
  }
233
301
 
234
302
  this.isListening = true;
@@ -250,7 +318,7 @@ module.exports = class Application extends Emitter {
250
318
  req.res = res;
251
319
  res.req = req;
252
320
 
253
- return this._middlewares.process(req, res)
321
+ return this._router.handle(req, res);
254
322
  }
255
323
 
256
324
 
@@ -288,5 +356,9 @@ module.exports = class Application extends Emitter {
288
356
 
289
357
  this.server.close();
290
358
  this.isListening = false;
359
+
360
+ this._router.unlock();
361
+ // Remove query parser.
362
+ this._router.remove.middleware(0);
291
363
  }
292
364
  }
@@ -0,0 +1,23 @@
1
+ const Enum = require('../enums/Enum');
2
+
3
+
4
+ module.exports = new Enum({
5
+ and: Symbol.for('and'),
6
+ between: Symbol.for('between'),
7
+ contains: Symbol.for('contains'),
8
+ eq: Symbol.for('eq'),
9
+ ne: Symbol.for('ne'),
10
+ gte: Symbol.for('gte'),
11
+ gt: Symbol.for('gt'),
12
+ lte: Symbol.for('lte'),
13
+ lt: Symbol.for('lt'),
14
+ not: Symbol.for('not'),
15
+ is: Symbol.for('is'),
16
+ in: Symbol.for('in'),
17
+ notIn: Symbol.for('notIn'),
18
+ like: Symbol.for('like'),
19
+ notLike: Symbol.for('notLike'),
20
+ notBetween: Symbol.for('notBetween'),
21
+ or: Symbol.for('or'),
22
+ xor: Symbol.for('xor'),
23
+ });
@@ -0,0 +1,194 @@
1
+
2
+ module.exports = {
3
+ getOne: _getOne,
4
+ getMany: _getMany,
5
+ createOne: _createOne,
6
+ updateOne: _updateOne,
7
+ deleteOne: _deleteOne
8
+ }
9
+
10
+
11
+ /*
12
+ * @alias getOne
13
+ * @api public
14
+ */
15
+ async function _getOne(req, res) {
16
+ try {
17
+ const params = {
18
+ query: {
19
+ ...req.query,
20
+ where: req.params
21
+ }
22
+ }
23
+ const result = await this.facade.getOne(params);
24
+
25
+ if (this.afterGetOne) {
26
+ await this.afterGetOne(req, res, result);
27
+ }
28
+
29
+ return this.respondOk(res, {
30
+ content: { ...result }
31
+ });
32
+ }
33
+ catch(error) {
34
+ if (this.processError) {
35
+ return this.processError(error, req, res);
36
+ }
37
+ else {
38
+ res.status(500);
39
+ res.json({ error: error.toString() });
40
+ }
41
+ }
42
+ finally {
43
+ return Promise.resolve();
44
+ }
45
+ }
46
+
47
+
48
+ /*
49
+ * @alias getMany
50
+ * @api public
51
+ */
52
+ async function _getMany(req, res) {
53
+ try {
54
+ const params = {
55
+ query: req.query
56
+ }
57
+ const result = await this.facade.getMany(params);
58
+
59
+ if (this.afterGetMany) {
60
+ await this.afterGetMany(req, res, result);
61
+ }
62
+
63
+ return this.respondOk(res, {
64
+ content: { ...result }
65
+ });
66
+ }
67
+ catch(error) {
68
+ if (this.processError) {
69
+ return this.processError(error, req, res);
70
+ }
71
+ else {
72
+ res.status(500);
73
+ res.json({ error: error.toString() });
74
+ }
75
+ }
76
+ finally {
77
+ return Promise.resolve();
78
+ }
79
+ }
80
+
81
+
82
+ /*
83
+ * @alias createOne
84
+ * @api public
85
+ */
86
+ async function _createOne(req, res) {
87
+ try {
88
+ const params = {
89
+ query: {
90
+ ...req.query,
91
+ where: req.params
92
+ },
93
+ data: req.body,
94
+ }
95
+ const result = await this.facade.createOne(params);
96
+
97
+ if (this.afterCreateOne) {
98
+ await this.afterCreateOne(req, res, result);
99
+ }
100
+
101
+ return this.respondOk(res, {
102
+ content: { ...result }
103
+ });
104
+ }
105
+ catch(error) {
106
+ if (this.processError) {
107
+ return this.processError(error, req, res);
108
+ }
109
+ else {
110
+ res.status(500);
111
+ res.json({ error: error.toString() });
112
+ }
113
+ }
114
+ finally {
115
+ return Promise.resolve();
116
+ }
117
+ }
118
+
119
+
120
+ /*
121
+ * @alias updateOne
122
+ * @api public
123
+ */
124
+ async function _updateOne(req, res) {
125
+ try {
126
+ const params = {
127
+ query: {
128
+ ...req.query,
129
+ where: req.params
130
+ },
131
+ data: req.body,
132
+ }
133
+ const result = await this.facade.updateOne(params);
134
+
135
+ if (this.afterUpdateOne) {
136
+ await this.afterUpdateOne(req, res, result);
137
+ }
138
+
139
+ return this.respondOk(res, {
140
+ content: { ...result }
141
+ });
142
+ }
143
+ catch(error) {
144
+ if (this.processError) {
145
+ return this.processError(error, req, res);
146
+ }
147
+ else {
148
+ res.status(500);
149
+ res.json({ error: error.toString() });
150
+ }
151
+ }
152
+ finally {
153
+ return Promise.resolve();
154
+ }
155
+ }
156
+
157
+
158
+ /*
159
+ * @alias deleteOne
160
+ * @api public
161
+ */
162
+ async function _deleteOne(req, res) {
163
+ try {
164
+ const params = {
165
+ query: {
166
+ ...req.query,
167
+ where: req.params
168
+ }
169
+ }
170
+ const result = await this.facade.deleteOne(params);
171
+
172
+ if (this.afterDeleteOn) {
173
+ await this.afterDeleteOne(req, res, result);
174
+ }
175
+
176
+ return this.respondOk(res, {
177
+ content: { ...result }
178
+ });
179
+ }
180
+ catch(error) {
181
+ if (this.processError) {
182
+ return this.processError(error, req, res);
183
+ }
184
+ else {
185
+ res.status(500);
186
+ res.json({ error: error.toString() });
187
+ }
188
+ }
189
+ finally {
190
+ return Promise.resolve();
191
+ }
192
+ }
193
+
194
+