nodester 0.0.8 → 0.1.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 (37) hide show
  1. package/Readme.md +18 -59
  2. package/lib/application/index.js +28 -7
  3. package/lib/controllers/methods/index.js +34 -10
  4. package/lib/controllers/mixins/index.js +14 -5
  5. package/lib/database/connection.js +34 -0
  6. package/lib/database/migration.js +42 -0
  7. package/lib/database/utils.js +19 -0
  8. package/lib/facades/methods/index.js +173 -0
  9. package/lib/facades/mixins/index.js +111 -0
  10. package/lib/middlewares/formidable/index.js +37 -0
  11. package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +2 -2
  12. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +3 -3
  13. package/lib/models/associate.js +17 -0
  14. package/lib/models/define.js +50 -1
  15. package/lib/models/mixins.js +81 -72
  16. package/lib/params/Params.js +10 -7
  17. package/lib/queries/Colander.js +84 -0
  18. package/lib/queries/traverse.js +311 -0
  19. package/lib/router/handlers.util.js +22 -2
  20. package/lib/router/index.js +96 -75
  21. package/lib/router/markers.js +78 -0
  22. package/lib/router/route.js +4 -4
  23. package/lib/router/routes.util.js +35 -5
  24. package/lib/router/utils.js +30 -0
  25. package/package.json +20 -7
  26. package/tests/nql.test.js +3 -3
  27. package/lib/_/n_controllers/Controller.js +0 -474
  28. package/lib/_/n_controllers/JWTController.js +0 -240
  29. package/lib/_/n_controllers/ServiceController.js +0 -109
  30. package/lib/_/n_controllers/WebController.js +0 -75
  31. package/lib/_facades/Facade.js +0 -388
  32. package/lib/_facades/FacadeParams.js +0 -11
  33. package/lib/_facades/ServiceFacade.js +0 -17
  34. package/lib/_facades/jwt.facade.js +0 -273
  35. package/lib/models/DisabledRefreshToken.js +0 -68
  36. package/lib/models/Extractor.js +0 -320
  37. package/lib/utils/forms.util.js +0 -22
package/Readme.md CHANGED
@@ -4,13 +4,13 @@
4
4
  ## Table of Contents
5
5
 
6
6
  - [Usage](#usage)
7
- - [Markers](#markers)
8
- - [Router](#router)
7
+ - [Documentation](#documentation)
9
8
  - [Extending App](#extending-application-functionality)
10
9
  - [Philosophy](#philosophy)
11
10
  - [License](#license)
12
11
  - [Copyright](#copyright)
13
12
 
13
+
14
14
  ## Usage
15
15
 
16
16
  ```js
@@ -25,72 +25,30 @@ app.listen(8080, function() {
25
25
  });
26
26
  ```
27
27
 
28
- ## Markers
29
-
30
- Marker is a functional condition that returns `true | false`, based on data in request/response.
31
-
32
- Markers are more powerful indicators than simple route definitions as any parameter in request/response can be used.
33
-
34
- For example: our application has 2 domains:
35
- `admin.awesomeapp.com`
36
- `api.awesomeapp.com`
37
-
38
- You add markers and handlers specifically for those domains:
39
-
40
- ```js
41
- app.add.marker('ADMIN', (req) => req.hostname === 'admin.awesomeapp.com');
42
- app.add.marker('API', (req) => req.hostname === 'api.awesomeapp.com');
43
- ```
44
-
45
- And then use them:
46
-
47
- ```js
48
- app.only('ADMIN').route('get /payments', <handler/>);
49
- app.only('API').route('get /payments', <handler/>);
50
- // Or:
51
- app.only('ADMIN').use(<handler/>);
52
- app.only('API').use(<handler/>);
53
- ```
54
-
55
- The same can be done for any parameter in request/response:
56
-
57
- ```js
58
- app.add.marker('admin_role', (req) => req.role === 'admin');
59
- app.only('admin_role').route('get /secrets', <handler/>);
60
- app.only('admin_role').use(<handler/>);
61
- ```
62
-
63
- ## Router
64
28
 
65
- Router is a built-in middleware.
29
+ ## Documentation
66
30
 
67
- ```js
68
- const Router = require('nodester/router');
69
31
 
70
- const controllersPath = <path_to_controllers_directory/>;
71
- const router = new Router({ controllersPath });
32
+ ### Core concepts
33
+ [Go to core concepts documentaion](docs/CoreConcepts.md)
72
34
 
73
- router.add.route('get /books', function(req, res) { ... } );
74
- // Or:
75
- router.add.route('get /books', { controlledBy: 'BooksController.getMany' } );
76
- router.add.route('get /books/:id', { controlledBy: 'BooksController.getOne' } );
77
- ```
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:
78
37
 
79
- ### Using Router:
38
+ [Go to NQR documentaion](docs/Queries.md)
80
39
 
81
- ```js
82
- const nodester = require('nodester');
83
- const router = require(<path_to_router_definition/>);
84
40
 
85
- const app = new nodester();
86
- app.use(router());
87
- ```
41
+ ### Database
42
+ Nodester is built upon a powerful [Sequelize](https://sequelize.org/).
43
+ Supported drivers:
44
+ - MySQL
45
+ - PostgreSQL
88
46
 
89
47
 
90
- ## Extending Application functionality
48
+ ### Extending Application functionality
91
49
 
92
50
 
93
- ### Extending instance (safe way):
51
+ #### Extending instance (safe way):
94
52
 
95
53
  ```js
96
54
  const serveStatic = require('serve-static');
@@ -124,7 +82,7 @@ app.static = serveStatic;
124
82
  But you'll never know if you did override any of the app's properties or did not.
125
83
 
126
84
 
127
- ### Extending class:
85
+ #### Extending class:
128
86
 
129
87
  If you really want to override properties or use `nodester` as a boilerplate, you should extend default Application class:
130
88
 
@@ -143,13 +101,14 @@ class MyApp extends NodesterApp {
143
101
  module.exports = MyApp;
144
102
  ```
145
103
 
104
+
146
105
  ## Philosophy
147
106
 
148
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.
149
108
 
150
109
  ### Goal
151
110
 
152
- The goal of `nodester` is to be a robust and flexible framework 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.
153
112
 
154
113
 
155
114
  ## LICENSE
@@ -7,17 +7,25 @@
7
7
 
8
8
  const Emitter = require('events');
9
9
  const DefaultRouter = require('../router');
10
+
10
11
  // Server:
11
12
  const http = require('http');
12
13
  const request = require('../http/request');
13
14
  const response = require('../http/response');
15
+
14
16
  // Middlewares:
15
17
  const nodesterQL = require('../middlewares/ql/sequelize');
18
+ const bodyParser = require('body-parser');
19
+
16
20
  // Utils:
17
21
  const {
18
22
  typeOf,
19
23
  isConstructor
20
24
  } = require('../utils/types.util');
25
+ const {
26
+ associateModels
27
+ } = require('../database/utils');
28
+
21
29
  const { merge } = require('../utils/objects.util');
22
30
  const consl = require('../logger/console');
23
31
  const debug = require('debug')('nodester:application');
@@ -52,6 +60,13 @@ module.exports = class Application extends Emitter {
52
60
  beforeStart: ()=>{}
53
61
  };
54
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
+
55
70
  // Indicatorors.
56
71
  this.isListening = false;
57
72
  }
@@ -104,7 +119,7 @@ module.exports = class Application extends Emitter {
104
119
  *
105
120
  * @public
106
121
  */
107
- setDatabase(sequilizeConnection, crashOnError=true) {
122
+ async setDatabase(sequilizeConnection, crashOnError=true) {
108
123
  try {
109
124
  if (!sequilizeConnection) {
110
125
  const err = new TypeError('Connection to database (Sequilize) can not be null or undefined.');
@@ -116,10 +131,14 @@ module.exports = class Application extends Emitter {
116
131
  throw err;
117
132
  }
118
133
 
119
- const result = sequilizeConnection.authenticate();
134
+ // Test connection.
135
+ const result = await sequilizeConnection.authenticate();
136
+ // Associate models.
137
+ await associateModels(sequilizeConnection.models);
138
+ // Set database.
120
139
  this._database = sequilizeConnection;
121
140
 
122
- return result;
141
+ return Promise.resolve(result);
123
142
  }
124
143
  catch(error) {
125
144
  if (crashOnError === true) {
@@ -128,6 +147,8 @@ module.exports = class Application extends Emitter {
128
147
  else {
129
148
  consl.error(error);
130
149
  }
150
+
151
+ return Promise.reject(error);
131
152
  }
132
153
  }
133
154
 
@@ -188,14 +209,14 @@ module.exports = class Application extends Emitter {
188
209
 
189
210
 
190
211
  /*
191
- * Proxy to .add.middleware()
212
+ * Proxy to router.use()
192
213
  *
193
- * @param {Function} fn
214
+ * @param {Function|NodesterRouter} fnOrRouter
194
215
  *
195
216
  * @api public
196
217
  */
197
- use(fn) {
198
- return this._router.add.middleware(fn);
218
+ use(fnOrRouter) {
219
+ return this._router.use(fnOrRouter);
199
220
  }
200
221
 
201
222
 
@@ -14,8 +14,13 @@ module.exports = {
14
14
  */
15
15
  async function _getOne(req, res) {
16
16
  try {
17
-
18
- const result = await this.facade.getOne();
17
+ const params = {
18
+ query: {
19
+ ...req.query,
20
+ where: req.params
21
+ }
22
+ }
23
+ const result = await this.facade.getOne(params);
19
24
 
20
25
  if (this.afterGetOne) {
21
26
  await this.afterGetOne(req, res, result);
@@ -26,7 +31,6 @@ async function _getOne(req, res) {
26
31
  });
27
32
  }
28
33
  catch(error) {
29
- console.error('getOne', error);
30
34
  if (this.processError) {
31
35
  return this.processError(error, req, res);
32
36
  }
@@ -47,7 +51,10 @@ async function _getOne(req, res) {
47
51
  */
48
52
  async function _getMany(req, res) {
49
53
  try {
50
- const result = await this.facade.getMany();
54
+ const params = {
55
+ query: req.query
56
+ }
57
+ const result = await this.facade.getMany(params);
51
58
 
52
59
  if (this.afterGetMany) {
53
60
  await this.afterGetMany(req, res, result);
@@ -78,8 +85,14 @@ async function _getMany(req, res) {
78
85
  */
79
86
  async function _createOne(req, res) {
80
87
  try {
81
-
82
- const result = await this.facade.createOne();
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);
83
96
 
84
97
  if (this.afterCreateOne) {
85
98
  await this.afterCreateOne(req, res, result);
@@ -110,8 +123,14 @@ async function _createOne(req, res) {
110
123
  */
111
124
  async function _updateOne(req, res) {
112
125
  try {
113
-
114
- const result = await this.facade.updateOne();
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);
115
134
 
116
135
  if (this.afterUpdateOne) {
117
136
  await this.afterUpdateOne(req, res, result);
@@ -142,8 +161,13 @@ async function _updateOne(req, res) {
142
161
  */
143
162
  async function _deleteOne(req, res) {
144
163
  try {
145
-
146
- const result = await this.facade.deleteOne();
164
+ const params = {
165
+ query: {
166
+ ...req.query,
167
+ where: req.params
168
+ }
169
+ }
170
+ const result = await this.facade.deleteOne(params);
147
171
 
148
172
  if (this.afterDeleteOn) {
149
173
  await this.afterDeleteOne(req, res, result);
@@ -134,6 +134,7 @@ function _withDefaultErrorProcessing(controller, opts={}) {
134
134
 
135
135
  // Set processError:
136
136
  controller.processError = function (error, req, res) {
137
+
137
138
  // Default error message.
138
139
  let errorMessage = error?.message ?? 'Internal server error';
139
140
  // Default HTTP status code.
@@ -169,23 +170,31 @@ function _withDefaultErrorProcessing(controller, opts={}) {
169
170
  }
170
171
  case('InternalValidationError'): {
171
172
  statusCode = 500;
172
- errorResponse.details = { message:'Error' };
173
+ errorResponse.details = { message: 'Error' };
173
174
  break;
174
175
  }
175
176
  default: {
176
- errorResponse.details = { message:errorMessage };
177
+ errorResponse.details = { message: errorMessage };
177
178
  break;
178
179
  }
179
180
  }
180
181
 
181
- // Send error response with provided status code.
182
- return this.respondNotOk(res, {
182
+ // Send error response with provided status code:
183
+ const data = {
183
184
  error: {
184
185
  ...errorResponse,
185
186
  code: statusCode
186
187
  },
187
188
  status: statusCode
188
- });
189
+ }
190
+
191
+ if (!!this.respondNotOk) {
192
+ return this.respondNotOk(res, data);
193
+ }
194
+
195
+ // Barebones response:
196
+ res.status(statusCode);
197
+ res.json(data);
189
198
  }
190
199
 
191
200
  return controller;
@@ -0,0 +1,34 @@
1
+ // ORM.
2
+ const Sequelize = require('sequelize');
3
+
4
+
5
+ module.exports = {
6
+ buildConnection: _buildConnection
7
+ };
8
+
9
+ function _buildConnection(opts={}) {
10
+
11
+
12
+ const dbName = opts.name;
13
+ const username = opts.username;
14
+ const password = opts.password;
15
+
16
+
17
+ const connection = new Sequelize(
18
+ dbName,
19
+ username,
20
+ password,
21
+ {
22
+ host: opts.host,
23
+ port: opts.port,
24
+ dialect: opts.dialect,
25
+ pool: opts.pool,
26
+ charset: opts.charset,
27
+ collate: opts.collate,
28
+ timestamps: opts.timestamps,
29
+ logging: opts.logging
30
+ }
31
+ );
32
+
33
+ return connection;
34
+ }
@@ -0,0 +1,42 @@
1
+ const { associateModels } = require('./utils');
2
+
3
+
4
+ module.exports = {
5
+ migrate: _migrate
6
+ }
7
+
8
+ async function _migrate(databaseConnection, force=false) {
9
+ try {
10
+ // Validation of 'force' parameter.
11
+ if (typeof force !== 'boolean') {
12
+ const err = new Error('Wrong "force" parameter; must be boolean.');
13
+ throw err;
14
+ }
15
+
16
+ // Test connection.
17
+ await databaseConnection.authenticate();
18
+
19
+
20
+ const models = databaseConnection.models;
21
+ const modelNames = Object.keys(models);
22
+ console.info('Models to sync:', modelNames);
23
+ console.info('Forcefully?', force);
24
+ console.info('Syncing...\n');
25
+ await associateModels(models);
26
+ await databaseConnection.sync({ force });
27
+ console.info('Successful migration!');
28
+
29
+ const output = {
30
+ synced: true,
31
+ modelNames: modelNames,
32
+ models: models
33
+ }
34
+ return Promise.resolve(output);
35
+ }
36
+ catch(error) {
37
+ console.error('Migration failed!');
38
+ console.error(error);
39
+ return Promise.reject(error);
40
+ }
41
+ }
42
+
@@ -0,0 +1,19 @@
1
+
2
+ module.exports = {
3
+ associateModels: _associateModels
4
+ }
5
+
6
+ async function _associateModels(models) {
7
+ return new Promise((resolve, reject) => {
8
+ try {
9
+ Object.keys(models).map(modelName => (
10
+ models[modelName].associate(models)
11
+ ));
12
+
13
+ return resolve(models);
14
+ }
15
+ catch(error) {
16
+ reject(error);
17
+ }
18
+ });
19
+ }
@@ -0,0 +1,173 @@
1
+ const Params = require('nodester/params');
2
+
3
+
4
+ module.exports = {
5
+ getOne: _getOne,
6
+ getMany: _getMany,
7
+ createOne: _createOne,
8
+ updateOne: _updateOne,
9
+ deleteOne: _deleteOne
10
+ }
11
+
12
+
13
+ /*
14
+ *
15
+ * @param {Object} params
16
+ *
17
+ * @alias getOne
18
+ * @api public
19
+ */
20
+ async function _getOne(params) {
21
+ try {
22
+ const {
23
+ query
24
+ } = Params(params, {
25
+ query: {}
26
+ });
27
+
28
+ const instance = await this.model.findOne(query);
29
+
30
+ const result = {
31
+ [this.modelName.singular]: instance,
32
+ count: 0 + instance !== null
33
+ }
34
+ return Promise.resolve(result);
35
+ }
36
+ catch(error) {
37
+ return Promise.reject(error);
38
+ }
39
+ }
40
+
41
+
42
+ /*
43
+ *
44
+ * @param {Object} params
45
+ *
46
+ * @alias getMany
47
+ * @api public
48
+ */
49
+ async function _getMany(params) {
50
+ try {
51
+ const {
52
+ query
53
+ } = Params(params, {
54
+ query: {}
55
+ });
56
+
57
+ const instances = await this.model.findAll(query);
58
+
59
+ const result = {
60
+ [this.modelName.plural]: instances,
61
+ count: instances.length
62
+ }
63
+ return Promise.resolve(result);
64
+ }
65
+ catch(error) {
66
+ return Promise.reject(error);
67
+ }
68
+ }
69
+
70
+
71
+ /*
72
+ *
73
+ * @param {Object} params
74
+ *
75
+ * @alias createOne
76
+ * @api public
77
+ */
78
+ async function _createOne(params) {
79
+ try {
80
+ const {
81
+ data,
82
+ includes,
83
+ } = Params(params, {
84
+ data: null,
85
+ includes: null,
86
+ });
87
+
88
+ const instance = await this.model.create({ ...data }, {
89
+ include: this.model.getIncludesList(data)
90
+ });
91
+
92
+ // If includes are set, "find" this record with includes:
93
+ if (!!includes && includes?.length > 0) {
94
+ await instance.reload({ include: includes });
95
+ }
96
+
97
+ const result = {
98
+ [this.modelName.singular]: instance,
99
+ count: instance === null ? 0 : 1
100
+ }
101
+
102
+ // Call after create.
103
+ await this.afterCreateOne(instance, params, result);
104
+
105
+ return Promise.resolve(result);
106
+ }
107
+ catch(error) {
108
+ return Promise.reject(error);
109
+ }
110
+ }
111
+
112
+
113
+ /*
114
+ *
115
+ * @param {Object} params
116
+ *
117
+ * @alias updateOne
118
+ * @api public
119
+ */
120
+ async function _updateOne(params) {
121
+ try {
122
+ const {
123
+ query,
124
+ data
125
+ } = Params(params, {
126
+ query: {},
127
+ data: null
128
+ });
129
+
130
+ const updateResult = await this.model.updateOne(query.where, data);;
131
+
132
+ const [ isNewRecord, instance ] = updateResult;
133
+
134
+ const result = {
135
+ success: isNewRecord === false,
136
+ [this.modelName.singular]: instance,
137
+ count: !!instance ? 1 : 0
138
+ }
139
+ return Promise.resolve(result);
140
+ }
141
+ catch(error) {
142
+ return Promise.reject(error);
143
+ }
144
+ }
145
+
146
+
147
+ /*
148
+ *
149
+ * @param {Object} params
150
+ *
151
+ * @alias deleteOne
152
+ * @api public
153
+ */
154
+ async function _deleteOne(params) {
155
+ try {
156
+ const {
157
+ query
158
+ } = Params(params, {
159
+ query: null
160
+ });
161
+
162
+ const count = await this.model.deleteOne(query);
163
+
164
+ const result = {
165
+ success: count > 0,
166
+ count: count
167
+ };
168
+ return Promise.resolve(result);
169
+ }
170
+ catch(error) {
171
+ return Promise.reject(error);
172
+ }
173
+ }