nodester 0.1.4 → 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 (63) hide show
  1. package/Readme.md +16 -55
  2. package/lib/application/index.js +174 -63
  3. package/lib/body/extract.js +89 -0
  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 +16 -16
  12. package/lib/facades/mixins/index.js +67 -13
  13. package/lib/factories/responses/rest.js +25 -13
  14. package/lib/http/codes/descriptions.js +82 -0
  15. package/lib/http/codes/index.js +70 -145
  16. package/lib/http/codes/symbols.js +82 -0
  17. package/lib/http/{request.js → request/index.js} +53 -75
  18. package/lib/http/request/utils.js +27 -0
  19. package/lib/http/response/headers.js +138 -0
  20. package/lib/http/response/index.js +248 -0
  21. package/lib/http/response/utils.js +38 -0
  22. package/lib/middlewares/SearchParams/index.js +25 -0
  23. package/lib/middlewares/cookies/index.js +44 -0
  24. package/lib/middlewares/etag/index.js +32 -15
  25. package/lib/middlewares/formidable/index.js +30 -25
  26. package/lib/middlewares/ql/sequelize/index.js +13 -4
  27. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +4 -3
  28. package/lib/middlewares/render/index.js +62 -0
  29. package/lib/models/associate.js +25 -1
  30. package/lib/models/define.js +26 -19
  31. package/lib/models/mixins.js +8 -1
  32. package/lib/{queries → query}/traverse.js +118 -77
  33. package/lib/router/handlers.util.js +1 -0
  34. package/lib/router/index.js +194 -99
  35. package/lib/router/markers.js +7 -0
  36. package/lib/router/route.js +5 -0
  37. package/lib/router/routes.util.js +16 -14
  38. package/lib/router/utils.js +7 -0
  39. package/lib/stacks/MarkersStack.js +41 -3
  40. package/lib/stacks/MiddlewaresStack.js +200 -0
  41. package/lib/structures/Enum.js +46 -0
  42. package/lib/structures/Filter.js +156 -0
  43. package/lib/structures/Params.js +55 -0
  44. package/lib/tools/sql.tool.js +7 -0
  45. package/lib/utils/objects.util.js +31 -24
  46. package/lib/utils/sanitizations.util.js +10 -4
  47. package/lib/validators/arguments.js +68 -0
  48. package/lib/validators/dates.js +7 -0
  49. package/lib/validators/numbers.js +7 -0
  50. package/package.json +20 -10
  51. package/lib/database/utils.js +0 -19
  52. package/lib/enums/Enum.js +0 -16
  53. package/lib/http/response.js +0 -1074
  54. package/lib/http/utils.js +0 -254
  55. package/lib/params/Params.js +0 -37
  56. package/lib/policies/Role.js +0 -77
  57. package/lib/policies/RoleExtracting.js +0 -97
  58. package/lib/preprocessors/BodyPreprocessor.js +0 -61
  59. package/lib/queries/Colander.js +0 -107
  60. package/lib/queries/NodesterQueryParams.js +0 -145
  61. package/lib/services/includes.service.js +0 -79
  62. package/lib/services/jwt.service.js +0 -147
  63. package/lib/stacks/MiddlewareStack.js +0 -159
@@ -1,3 +1,10 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
  const {
2
9
  NODESTER_QUERY_ERROR,
3
10
  UNAUTHORIZED_ERROR,
@@ -16,6 +23,9 @@ const {
16
23
  deleteOne
17
24
  } = require('../methods');
18
25
 
26
+ // Arguments validator.
27
+ const { ensure } = require('../../validators/arguments');
28
+
19
29
 
20
30
  module.exports = {
21
31
  withDefaultCRUD: _withDefaultCRUD,
@@ -30,29 +40,27 @@ module.exports = {
30
40
  * Sets one of or all of CRUD methods to Controller.
31
41
  *
32
42
  * @param {Function|Object} controller
33
- * @param {Object} opts
34
- * - @param {Function|Object} facade
35
- * - @param {String} name
36
- * - @param {Array} only
43
+ * @param {Object} options
44
+ * @param {Function|Object} options.facade
45
+ * @param {String} options.name
46
+ * @param {Array} options.only
37
47
  *
38
48
  * @return {Function|Object} controller
39
49
  *
40
50
  * @api public
41
51
  * @alias withDefaultCRUD
42
52
  */
43
- function _withDefaultCRUD(controller, opts={}) {
53
+ function _withDefaultCRUD(controller, options={}) {
54
+ ensure(controller, 'function|object,required', 'controller');
55
+ ensure(options, 'object,required', 'options');
56
+
44
57
  const {
45
58
  facade,
46
59
 
47
60
  // Optional:
48
61
  name,
49
62
  only
50
- } = opts;
51
-
52
- if (!controller) {
53
- const err = new TypeError(`'controller' argument is not provided.`);
54
- throw err;
55
- }
63
+ } = options;
56
64
 
57
65
  _setFacade(controller, facade);
58
66
 
@@ -60,7 +68,7 @@ function _withDefaultCRUD(controller, opts={}) {
60
68
  const model = facade.model;
61
69
  // Extract plural name of this model.
62
70
  const modelPluralName = model?.options?.name?.plural;
63
- // Set name of this controller:
71
+ // Set read-only name of this controller:
64
72
  Object.defineProperty(controller, 'name', {
65
73
  value: name ?? `${ modelPluralName ?? controller.name }Controller`,
66
74
  writable: false
@@ -120,18 +128,15 @@ function _withDefaultCRUD(controller, opts={}) {
120
128
  * Sets default error responses to Controller.
121
129
  *
122
130
  * @param {Function|Object} controller
123
- * @param {Object} opts
131
+ * @param {Object} options
124
132
  *
125
133
  * @return {Function|Object} controller
126
134
  *
127
135
  * @api public
128
136
  * @alias withDefaultErrorProcessing
129
137
  */
130
- function _withDefaultErrorProcessing(controller, opts={}) {
131
- if (!controller) {
132
- const err = new TypeError(`'controller' argument is not provided.`);
133
- throw err;
134
- }
138
+ function _withDefaultErrorProcessing(controller, options={}) {
139
+ ensure(controller, 'function|object,required', 'controller');
135
140
 
136
141
  // Set processError:
137
142
  controller.processError = function (error, req, res) {
@@ -220,15 +225,8 @@ function _withDefaultErrorProcessing(controller, opts={}) {
220
225
  * @alias setFacade
221
226
  */
222
227
  function _setFacade(controller, facade, facadeName=null) {
223
- if (!controller) {
224
- const err = new TypeError(`'controller' argument is not provided.`);
225
- throw err;
226
- }
227
-
228
- if (!facade) {
229
- const err = new TypeError(`'facade' argument is invalid.`);
230
- throw err;
231
- }
228
+ ensure(controller, 'function|object,required', 'controller');
229
+ ensure(facade, 'function|object,required', 'facade');
232
230
 
233
231
  // Set main facade:
234
232
  if (facade.constructor.name === 'Function') {
@@ -237,25 +235,27 @@ function _setFacade(controller, facade, facadeName=null) {
237
235
  else {
238
236
  controller[facadeName ?? 'facade'] = facade;
239
237
  }
238
+
239
+ return controller;
240
240
  }
241
241
 
242
242
 
243
243
  /**
244
244
  * Sets one of CRUD methods to Controller.
245
245
  *
246
- * @param {Function|Object} controller
247
- * @param {Object} opts
246
+ * @param {Controller} controller
247
+ * @param {Function} fn
248
248
  *
249
- * @return {Function|Object} controller
249
+ * @return {Controller} controller
250
250
  *
251
251
  * @api public
252
- * @alias withDefaultCRUD
252
+ * @alias setMethod
253
253
  */
254
- function _setMethod(controller,) {
255
- if (!controller) {
256
- const err = new TypeError(`'controller' argument is not provided.`);
257
- throw err;
258
- }
254
+ function _setMethod(controller, fn) {
255
+ ensure(controller, 'function|object,required', 'controller');
256
+ ensure(fn, 'function,required', 'fn');
259
257
 
258
+ controller[fn] = fn.bind(controller);
260
259
 
260
+ return controller;
261
261
  }
@@ -1,3 +1,9 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+ 'use strict';
6
+
1
7
  // ORM.
2
8
  const Sequelize = require('sequelize');
3
9
 
@@ -1,4 +1,11 @@
1
- const { associateModels } = require('./utils');
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const { associateModels } = require('nodester/models/associate');
2
9
 
3
10
 
4
11
  module.exports = {
@@ -19,10 +26,13 @@ async function _migrate(databaseConnection, force=false) {
19
26
 
20
27
  const models = databaseConnection.models;
21
28
  const modelNames = Object.keys(models);
22
- console.info('Models to sync:', modelNames);
29
+
30
+ console.info('Models list:', ...modelNames.map(name => `\n • ${ name }`), '\n');
31
+
23
32
  console.info('Forcefully?', force);
24
33
  console.info('Syncing...\n');
25
- await associateModels(models);
34
+
35
+ await associateModels(databaseConnection);
26
36
  await databaseConnection.sync({ force });
27
37
  console.info('Successful migration!');
28
38
 
@@ -34,7 +44,7 @@ async function _migrate(databaseConnection, force=false) {
34
44
  return Promise.resolve(output);
35
45
  }
36
46
  catch(error) {
37
- console.error('Migration failed!');
47
+ console.error(' Migration failed!');
38
48
  console.error(error);
39
49
  return Promise.reject(error);
40
50
  }
@@ -1,3 +1,10 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
  const Params = require('nodester/params');
2
9
 
3
10
  const log = require('nodester/loggers/dev');
@@ -30,13 +37,13 @@ async function _getOne(params) {
30
37
  const instance = await this.model.findOne(query);
31
38
 
32
39
  const result = {
33
- [this.modelName.singular]: instance,
40
+ [this.outputName.singular]: instance,
34
41
  count: 0 + (instance !== null)
35
42
  }
36
43
  return Promise.resolve(result);
37
44
  }
38
45
  catch(error) {
39
- log.error(`${ [this.modelName.singular] }Facade.getOne error:`, error);
46
+ log.error(`${ this.name }.getOne error:`, error);
40
47
  return Promise.reject(error);
41
48
  }
42
49
  }
@@ -60,13 +67,13 @@ async function _getMany(params) {
60
67
  const instances = await this.model.findAll(query);
61
68
 
62
69
  const result = {
63
- [this.modelName.plural]: instances,
70
+ [this.outputName.plural]: instances,
64
71
  count: instances.length
65
72
  }
66
73
  return Promise.resolve(result);
67
74
  }
68
75
  catch(error) {
69
- log.error(`${ [this.modelName.singular] }Facade.getMany error:`, error);
76
+ log.error(`${ this.name }.getMany error:`, error);
70
77
  return Promise.reject(error);
71
78
  }
72
79
  }
@@ -83,23 +90,16 @@ async function _createOne(params) {
83
90
  try {
84
91
  const {
85
92
  data,
86
- includes,
87
93
  } = Params(params, {
88
94
  data: null,
89
- includes: null,
90
95
  });
91
96
 
92
97
  const instance = await this.model.create({ ...data }, {
93
98
  include: this.model.getIncludesList(data)
94
99
  });
95
100
 
96
- // If includes are set, "find" this record with includes:
97
- if (!!includes && includes?.length > 0) {
98
- await instance.reload({ include: includes });
99
- }
100
-
101
101
  const result = {
102
- [this.modelName.singular]: instance,
102
+ [this.outputName.singular]: instance,
103
103
  count: 0 + (instance !== null)
104
104
  }
105
105
 
@@ -109,7 +109,7 @@ async function _createOne(params) {
109
109
  return Promise.resolve(result);
110
110
  }
111
111
  catch(error) {
112
- log.error(`${ [this.modelName.singular] }Facade.createOne error:`, error);
112
+ log.error(`${ this.name }.createOne error:`, error);
113
113
  return Promise.reject(error);
114
114
  }
115
115
  }
@@ -138,13 +138,13 @@ async function _updateOne(params) {
138
138
 
139
139
  const result = {
140
140
  success: isNewRecord === false,
141
- [this.modelName.singular]: instance,
141
+ [this.outputName.singular]: instance,
142
142
  count: 0 + (instance !== null)
143
143
  }
144
144
  return Promise.resolve(result);
145
145
  }
146
146
  catch(error) {
147
- log.error(`${ [this.modelName.singular] }Facade.updateOne error:`, error);
147
+ log.error(`${ this.name }.updateOne error:`, error);
148
148
  return Promise.reject(error);
149
149
  }
150
150
  }
@@ -174,7 +174,7 @@ async function _deleteOne(params) {
174
174
  return Promise.resolve(result);
175
175
  }
176
176
  catch(error) {
177
- log.error(`${ [this.modelName.singular] }Facade.deleteOne error:`, error);
177
+ log.error(`${ this.name }.deleteOne error:`, error);
178
178
  return Promise.reject(error);
179
179
  }
180
180
  }
@@ -1,3 +1,10 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
  const {
2
9
  getOne,
3
10
  getMany,
@@ -5,41 +12,47 @@ const {
5
12
  updateOne,
6
13
  deleteOne
7
14
  } = require('../methods');
8
- // Utils,
15
+
16
+ // Utils:
17
+ const { Sequelize } = require('sequelize');
9
18
  const { lowerCaseFirstLetter } = require('nodester/utils/strings');
10
19
 
20
+ // Arguments validator.
21
+ const { ensure } = require('../../validators/arguments');
22
+
11
23
 
12
24
  module.exports = {
13
25
  withDefaultCRUD: _withDefaultCRUD,
26
+
27
+ setMethod: _setMethod
14
28
  }
15
29
 
16
30
  /**
17
31
  * Sets one of or all of CRUD methods to Facade.
18
32
  *
19
33
  * @param {Function|Object} facade
20
- * @param {Object} opts
21
- * - @param {Function|Object} model
22
- * - @param {String} name
23
- * - @param {Array} only
34
+ * @param {Object} options
35
+ * @param {Function|Object} options.model
36
+ * @param {String} options.name
37
+ * @param {Array} options.only
24
38
  *
25
39
  * @return {Function|Object} facade
26
40
  *
27
41
  * @api public
28
42
  * @alias withDefaultCRUD
29
43
  */
30
- function _withDefaultCRUD(facade, opts={}) {
44
+ function _withDefaultCRUD(facade, options={}) {
45
+ ensure(facade, 'function|object,required', 'facade');
46
+ ensure(options, 'object,required', 'options');
47
+ ensure(options.model, 'function|object,required', 'options.model');
48
+
31
49
  const {
32
50
  model,
33
51
 
34
52
  // Optional:
35
53
  name,
36
54
  only
37
- } = opts;
38
-
39
- if (!facade) {
40
- const err = new TypeError(`'facade' argument is not provided.`);
41
- throw err;
42
- }
55
+ } = options;
43
56
 
44
57
  // Set model info:
45
58
  // Set model:
@@ -58,7 +71,6 @@ function _withDefaultCRUD(facade, opts={}) {
58
71
  writable: false
59
72
  });
60
73
 
61
-
62
74
  // Set name of this facade:
63
75
  Object.defineProperty(facade, 'name', {
64
76
  value: name ?? `${ modelName.plural ?? facade.name }Facade`,
@@ -66,6 +78,28 @@ function _withDefaultCRUD(facade, opts={}) {
66
78
  });
67
79
 
68
80
 
81
+ // Set the name of the output:
82
+ const outputName = {
83
+ singular: Sequelize.Utils.underscore(modelName.singular),
84
+ plural: Sequelize.Utils.underscore(modelName.plural)
85
+ }
86
+
87
+ switch (model.options.nodester.output) {
88
+ case 'camelcased':
89
+ outputName.singular = this.modelName.singular;
90
+ outputName.plural = this.modelName.plural;
91
+ break;
92
+ case 'underscored':
93
+ default:
94
+ break;
95
+ }
96
+
97
+ Object.defineProperty(facade, 'outputName', {
98
+ value: outputName,
99
+ writable: false
100
+ });
101
+
102
+
69
103
  // If only certain methods should be set:
70
104
  if (!!only) {
71
105
  for (const selectedMethod of only) {
@@ -109,3 +143,23 @@ function _withDefaultCRUD(facade, opts={}) {
109
143
 
110
144
  return facade;
111
145
  }
146
+
147
+ /**
148
+ * Sets one of CRUD methods to Facade.
149
+ *
150
+ * @param {Facade} facade
151
+ * @param {Function} fn
152
+ *
153
+ * @return {Facade} facade
154
+ *
155
+ * @api public
156
+ * @alias setMethod
157
+ */
158
+ function _setMethod(facade, fn) {
159
+ ensure(facade, 'function|object,required', 'facade');
160
+ ensure(fn, 'function,required', 'fn');
161
+
162
+ facade[fn] = fn.bind(facade);
163
+
164
+ return facade;
165
+ }
@@ -1,5 +1,12 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
  /*
2
- * Rest response factory.
9
+ * REST response factory.
3
10
  */
4
11
 
5
12
  const ResponseFormats = require('../../constants/ResponseFormats');
@@ -11,7 +18,6 @@ module.exports = {
11
18
  createErrorResponse: _createErrorResponse,
12
19
  }
13
20
 
14
-
15
21
  /*
16
22
  * Format for all API responses will be JSON
17
23
  * {
@@ -25,6 +31,10 @@ module.exports = {
25
31
  *
26
32
  * @param {ServerResponse} res
27
33
  * @param {Object} options
34
+ * @param {Object} options.error
35
+ * @param {Object} options.content (optional)
36
+ * @param {Int} options.status
37
+ * @param {String} options.format
28
38
  *
29
39
  * @alias createGenericResponse
30
40
  * @api public
@@ -45,12 +55,13 @@ function _createGenericResponse(
45
55
  };
46
56
 
47
57
  switch(options?.format) {
48
- case ResponseFormats.JSON:
49
- return options?.res.status(options?.status).json(data);
50
- case ResponseFormats.XML:
58
+ case ResponseFormats.JSON: {
59
+ return res.json(data);
60
+ }
61
+ case ResponseFormats.XML: {
51
62
  // TODO: format data into XML.
52
- return options?.res.status(options?.status).send(data);
53
- break;
63
+ return res.send(data);
64
+ }
54
65
  default: {
55
66
  const err = new TypeError("No format specified.");
56
67
  throw err;
@@ -71,8 +82,8 @@ function _createGenericResponse(
71
82
  * Should be called on all successful respones.
72
83
  *
73
84
  * @param {ServerResponse} res
74
- * @param <Object> content
75
- * @param <String> format
85
+ * @param {Object} options.content (optional)
86
+ * @param {String} options.format
76
87
  *
77
88
  * @alias createOKResponse
78
89
  * @api public
@@ -92,10 +103,11 @@ function _createOKResponse(res, options={}) {
92
103
  * Should be called on all failed respones.
93
104
  *
94
105
  * @param {ServerResponse} res
95
- * @param <Object> error
96
- * @param <Object> content (optional)
97
- * @param <Int> status
98
- * @param <String> format
106
+ * @param {Object} options
107
+ * @param {Object} options.error
108
+ * @param {Object} options.content (optional)
109
+ * @param {Int} options.status
110
+ * @param {String} options.format
99
111
  *
100
112
  * @alias createErrorResponse
101
113
  * @api public
@@ -0,0 +1,82 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+ 'use strict';
6
+
7
+ const Enum = require('nodester/enum');
8
+
9
+
10
+ module.exports = new Enum({
11
+ // Informational:
12
+ 100: 'Continue',
13
+ 101: 'Switching Protocols',
14
+ 102: 'Processing',
15
+ 103: 'Early Hints',
16
+
17
+ // Success:
18
+ 200: 'OK',
19
+ 201: 'Created',
20
+ 202: 'Accepted',
21
+ 203: 'Non-Authoritative Information',
22
+ 204: 'No Content',
23
+ 205: 'Reset Content',
24
+ 206: 'Partial Content',
25
+ 207: 'Multi-Status',
26
+ 208: 'Already Reported',
27
+ 226: 'IM Used',
28
+
29
+ // Redirections:
30
+ 300: 'Multiple Choices',
31
+ 301: 'Moved Permanently',
32
+ 302: 'Found',
33
+ 303: 'See Other',
34
+ 304: 'Not Modified',
35
+ 305: 'Use Proxy',
36
+ 307: 'Temporary Redirect',
37
+ 308: 'Permanent Redirect',
38
+
39
+ // Client Errors:
40
+ 400: 'Bad Request',
41
+ 401: 'Unauthorized',
42
+ 402: 'Payment Required',
43
+ 403: 'Forbidden',
44
+ 404: 'Not Found',
45
+ 405: 'Method Not Allowed',
46
+ 406: 'Not Acceptable',
47
+ 407: 'Proxy Authentication Required',
48
+ 408: 'Request Timeout',
49
+ 409: 'Conflict',
50
+ 410: 'Gone',
51
+ 411: 'Length Required',
52
+ 412: 'Precondition Failed',
53
+ 413: 'Payload Too Large',
54
+ 414: 'URI Too Long',
55
+ 415: 'Unsupported Media Type',
56
+ 416: 'Range Not Satisfiable',
57
+ 417: 'Expectation Failed',
58
+ 418: 'I\'m a teapot',
59
+ 421: 'Misdirected Request',
60
+ 422: 'Unprocessable Entity',
61
+ 423: 'Locked',
62
+ 424: 'Failed Dependency',
63
+ 425: 'Too Early',
64
+ 426: 'Upgrade Required',
65
+ 428: 'Precondition Required',
66
+ 429: 'Too Many Requests',
67
+ 431: 'Request Header Fields Too Large',
68
+ 451: 'Unavailable For Legal Reasons',
69
+
70
+ // Server Errors:
71
+ 500: 'Internal Server Error',
72
+ 501: 'Not Implemented',
73
+ 502: 'Bad Gateway',
74
+ 503: 'Service Unavailable',
75
+ 504: 'Gateway Timeout',
76
+ 505: 'HTTP Version Not Supported',
77
+ 506: 'Variant Also Negotiates',
78
+ 507: 'Insufficient Storage',
79
+ 508: 'Loop Detected',
80
+ 510: 'Not Extended',
81
+ 511: 'Network Authentication Required'
82
+ });