nodester 0.1.5 → 0.2.1

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 (60) hide show
  1. package/Readme.md +25 -61
  2. package/lib/application/index.js +185 -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 +10 -2
  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 +97 -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 +157 -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/tests/index.test.js +4 -4
  49. package/tests/nql.test.js +18 -1
  50. package/lib/database/utils.js +0 -19
  51. package/lib/enums/Enum.js +0 -16
  52. package/lib/filters/Filter.js +0 -109
  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/services/includes.service.js +0 -79
  59. package/lib/services/jwt.service.js +0 -147
  60. package/lib/stacks/MiddlewareStack.js +0 -159
@@ -0,0 +1,200 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const finalhandler = require('finalhandler');
9
+
10
+ // Arguments validator.
11
+ const { ensure } = require('../validators/arguments');
12
+
13
+ // Console:
14
+ const consl = require('nodester/loggers/console');
15
+ const debug = require('debug')('nodester:MiddlewaresStack');
16
+
17
+
18
+ /*
19
+ * Creates new MiddlewaresStack.
20
+ *
21
+ * @param {Object} [opts]
22
+ * @param {Boolean} opts.finalhandlerEnabled
23
+ *
24
+ * @return {MiddlewaresStack}
25
+ *
26
+ */
27
+ module.exports = class MiddlewaresStack {
28
+ constructor(opts={}) {
29
+ // This array MUST stay flat!
30
+ this._middlewares = [];
31
+
32
+ // Indicates whether we can add more middlewares or no.
33
+ this._isLocked = false;
34
+ this.finalhandlerEnabled = !!opts.finalhandlerEnabled;
35
+
36
+
37
+ const env = process.env.NODE_ENV || 'development';
38
+ // Final middleware & error handler.
39
+ this.finalhandler = (req, res) => finalhandler(req, res, {
40
+ env: env,
41
+ onerror: consl.error.bind(this)
42
+ });
43
+ }
44
+
45
+ // Getters:
46
+ get middlewares() {
47
+ return this._middlewares;
48
+ }
49
+
50
+ get length() {
51
+ return this._middlewares.length;
52
+ }
53
+
54
+ /**
55
+ * Indicates whether user can add more middlewares or not.
56
+ *
57
+ * @return {Boolean} isLocked
58
+ *
59
+ * @api public
60
+ */
61
+ get isLocked() {
62
+ return this._isLocked === true;
63
+ }
64
+ // Getters\
65
+
66
+
67
+ /**
68
+ * Add the given middleware `fn` to the stack.
69
+ *
70
+ * @param {Function} fn
71
+ * @param {Integer} index (0 or undefined)
72
+ *
73
+ * @return {Integer} index of the new middleware
74
+ *
75
+ * @api public
76
+ */
77
+ add(fn, index) {
78
+ try {
79
+ ensure(fn, 'function,required', 'fn');
80
+ ensure(index, 'number', 'index');
81
+
82
+ if (this._isLocked) {
83
+ const err = new Error(`Can't add more middlewares while stack is locked.`);
84
+ throw err;
85
+ }
86
+
87
+ let pushedIndex = -1;
88
+
89
+ if (index === 0) {
90
+ this._middlewares.unshift(fn);
91
+ pushedIndex = 0;
92
+ }
93
+ else {
94
+ pushedIndex = this.middlewares.push(fn);
95
+ }
96
+
97
+ debug(`added middleware (${ pushedIndex })`);
98
+ return pushedIndex;
99
+ }
100
+ catch(error) {
101
+ Error.captureStackTrace(error, this.add);
102
+ throw error;
103
+ }
104
+ }
105
+
106
+
107
+ /**
108
+ * Removes middleware at index.
109
+ *
110
+ * @param {Integer} index
111
+ *
112
+ * @return {MiddlewaresStack} self
113
+ *
114
+ * @api public
115
+ */
116
+ remove(index=-1) {
117
+ try {
118
+ ensure(index, 'number,required', 'index');
119
+
120
+ if (this._isLocked) {
121
+ const err = new Error(`Can't remove middlewares while stack is locked.`);
122
+ throw err;
123
+ }
124
+
125
+ this._middlewares.splice(index, 1);
126
+ debug(`removed middleware (${ index })`);
127
+ return this;
128
+ }
129
+ catch(error) {
130
+ Error.captureStackTrace(error, this.remove);
131
+ throw error;
132
+ }
133
+ }
134
+
135
+
136
+ /*
137
+ * Prepare stack for processing.
138
+ *
139
+ * @api public
140
+ */
141
+ lock() {
142
+ if (this.finalhandlerEnabled) {
143
+ // Add final handler to the stack.
144
+ this.add((req, res)=>this.finalhandler(req, res)());
145
+ }
146
+
147
+ // Stack is ready.
148
+ this._isLocked = true;
149
+
150
+ debug(`stack is locked`);
151
+ }
152
+
153
+
154
+ /*
155
+ * Unlocks stack.
156
+ *
157
+ * @api public
158
+ */
159
+ unlock() {
160
+ this._isLocked = false;
161
+
162
+ if (this.finalhandlerEnabled) {
163
+ this._middlewares.pop();
164
+ }
165
+
166
+ debug(`stack is unlocked`);
167
+ }
168
+
169
+
170
+ /*
171
+ * Start chain.
172
+ *
173
+ * @api public
174
+ */
175
+ process(req, res, next) {
176
+ let middlewareOffset = -1;
177
+
178
+ const _next = (...args) => {
179
+ middlewareOffset += 1;
180
+ const fn = this._middlewares[middlewareOffset];
181
+
182
+ try {
183
+ if (!fn && !!next) {
184
+ // Middleware stack is finished:
185
+ return next.call(null, req, res, next, ...args);
186
+ }
187
+ else if (!!fn) {
188
+ return fn.call(null, req, res, _next, ...args);
189
+ }
190
+ }
191
+ catch(error) {
192
+ return this.finalhandler(req, res)(error);
193
+ }
194
+ }
195
+
196
+ // Initial start:
197
+ return _next();
198
+ }
199
+
200
+ }
@@ -0,0 +1,46 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+
9
+ module.exports = function Enum(constantsList={}) {
10
+ _setGetters(this, constantsList);
11
+
12
+ this.withValuePrefix = _withValuePrefix.bind(this);
13
+ this.withKeyPrefix = _withKeyPrefix.bind(this);
14
+ }
15
+
16
+ function _setGetters(enumTarget, constantsList) {
17
+ enumTarget.list = constantsList;
18
+
19
+ Object.keys(constantsList).forEach(key => {
20
+ enumTarget[key] = constantsList[key];
21
+ });
22
+
23
+ // Set constants in static array.
24
+ enumTarget.asArray = Object.values(constantsList);
25
+ }
26
+
27
+
28
+ function _withValuePrefix(prefix='') {
29
+ const constantsList = {};
30
+
31
+ for (const [ key, value ] of Object.entries(this.list)) {
32
+ constantsList[key] = `${ prefix }${ value }`;
33
+ }
34
+
35
+ _setGetters(this, constantsList);
36
+
37
+ return this;
38
+ }
39
+
40
+ function _withKeyPrefix(prefix='') {
41
+ Object.keys(this.list).forEach(key => {
42
+ this[`${ prefix }${ key }`] = this.list[key];
43
+ });
44
+
45
+ return this;
46
+ }
@@ -0,0 +1,157 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const BOUNDS = require('../constants/Bounds');
9
+ const CLAUSES = require('../constants/Clauses');
10
+
11
+ const { isModel } = require('../utils/models');
12
+ const { ensure } = require('../validators/arguments');
13
+
14
+
15
+ module.exports = class NodesterFilter {
16
+
17
+ /*
18
+ *
19
+ * @param {Model} model
20
+ * @param {Object} options
21
+ * @param {Array} options.fields
22
+ * @param {Array} options.clauses
23
+ * @param {Object} options.includes
24
+ *
25
+ * @param {Object} options.bounds
26
+ * @param {Object} options.bounds.fields
27
+ * @param {Object} options.bounds.clauses
28
+ *
29
+ * @param {Object} options.statics
30
+ * @param {Object} options.statics.attributes
31
+ * @param {Object} options.statics.clauses
32
+ *
33
+ *
34
+ */
35
+ constructor(model=null, options={}) {
36
+ ensure(options, 'object,required', 'options');
37
+
38
+ this._model = model;
39
+
40
+ this._fields = [];
41
+ this._clauses = [];
42
+ this._includes = {};
43
+
44
+ this._bounds = {
45
+ fields: {},
46
+ clauses: {}
47
+ }
48
+
49
+ this._statics = {
50
+ attributes: {},
51
+ clauses: {}
52
+ }
53
+
54
+ // If model is present:
55
+ if (isModel(this.model)) {
56
+ this._fields = Object.keys(this.model.tableAttributes);
57
+ this._clauses = CLAUSES.asArray;
58
+
59
+ // ...and no 'bounds' and 'statics' are provided,
60
+ // set defaults:
61
+ if (!options.bounds && !options.statics) {
62
+ this._bounds.clauses.limit = {
63
+ min: BOUNDS.limit.min,
64
+ max: BOUNDS.limit.max
65
+ }
66
+ }
67
+ }
68
+
69
+ const {
70
+ fields,
71
+ clauses,
72
+ includes,
73
+ bounds,
74
+ statics,
75
+ } = options;
76
+
77
+
78
+ // If fields are array:
79
+ if (Array.isArray(fields)) {
80
+ this._fields = fields;
81
+ }
82
+
83
+ if (Array.isArray(clauses)) {
84
+ this._clauses = clauses;
85
+ }
86
+
87
+ // Includes:
88
+ if (typeof includes === 'object') {
89
+ const { associations } = this.model;
90
+ for (const [ includeName, includeFilter ] of Object.entries(includes)) {
91
+ const association = associations[includeName];
92
+ // Validate association by name:
93
+ if (association === undefined) {
94
+ const error = new TypeError(`No include named '${ includeName }'.`);
95
+ Error.captureStackTrace(error, this.constructor);
96
+ throw error;
97
+ }
98
+
99
+ // If singular association:
100
+ // Fix of "Only HasMany associations support include.separate"
101
+ if ('HasMany' !== association.associationType) {
102
+ // Empty bounds.
103
+ includeFilter.noBounds = true;
104
+ }
105
+
106
+ this._includes[includeName] = includeFilter;
107
+ }
108
+ }
109
+ // Includes\
110
+
111
+ if (typeof bounds === 'object') {
112
+ this._bounds = bounds;
113
+ }
114
+
115
+ if (typeof statics === 'object') {
116
+ if (typeof statics.attributes === 'object') {
117
+ this._statics.attributes = statics.attributes;
118
+ }
119
+
120
+ if (typeof statics.clauses === 'object') {
121
+ this._statics.clauses = statics.clauses;
122
+ }
123
+ }
124
+ }
125
+
126
+ // Getters:
127
+ get fields() {
128
+ return this._fields;
129
+ }
130
+
131
+ get clauses() {
132
+ return this._clauses;
133
+ }
134
+
135
+ get includes() {
136
+ return this._includes;
137
+ }
138
+
139
+ get bounds() {
140
+ return this._bounds;
141
+ }
142
+
143
+ get statics() {
144
+ return this._statics;
145
+ }
146
+
147
+ get model() {
148
+ return this._model;
149
+ }
150
+ // Getters\
151
+
152
+ set noBounds(value) {
153
+ if (value === true) {
154
+ this._bounds.clauses = {};
155
+ }
156
+ }
157
+ }
@@ -0,0 +1,55 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const {
9
+ BOOLEAN
10
+ } = require('nodester/utils/sanitizations');
11
+
12
+
13
+ module.exports = Params;
14
+
15
+ /**
16
+ * Extracts only values set in the second argument from the first argument.
17
+ * If such values is missing in the first argument, will fallback to the value in the second argument.
18
+ *
19
+ * @param {Object} sourceObj
20
+ * @param {Object} defaultValuesList
21
+ *
22
+ * @return {Object} result
23
+ *
24
+ * @api public
25
+ */
26
+ function Params(
27
+ sourceObj={},
28
+ defaultValuesList={}
29
+ ) {
30
+ const result = {};
31
+
32
+ const keys = Object.keys(defaultValuesList);
33
+ for (const key of keys) {
34
+
35
+ // If value is not set,
36
+ // use default one from 'defaultValuesList':
37
+ if (sourceObj[key] === undefined) {
38
+ result[key] = defaultValuesList[key];
39
+ continue;
40
+ }
41
+
42
+ // If String:
43
+ if (typeof defaultValuesList[key] === 'string') {
44
+ result[key] = `${ sourceObj[key] }`;
45
+ continue;
46
+ }
47
+
48
+ const isBoolean = toString.call(defaultValuesList[key]) === '[object Boolean]';
49
+ const isNumber = !isNaN(`${ defaultValuesList[key] }`);
50
+
51
+ result[key] = isBoolean ? BOOLEAN(sourceObj[key]) : (isNumber ? parseFloat(sourceObj[key]) : sourceObj[key]);
52
+ }
53
+
54
+ return result;
55
+ }
@@ -1,3 +1,10 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
  const { QueryTypes } = require('sequelize');
2
9
  // Utils:
3
10
  const fs = require('fs');
@@ -2,8 +2,12 @@
2
2
  * /nodester
3
3
  * MIT Licensed
4
4
  */
5
+
5
6
  'use strict';
6
7
 
8
+ // Arguments validator.
9
+ const { ensure } = require('../validators/arguments');
10
+
7
11
 
8
12
  module.exports = {
9
13
  copyWithCertainAttributes: _copyWithCertainAttributes,
@@ -13,57 +17,60 @@ module.exports = {
13
17
  /**
14
18
  * Copy key-value of target object
15
19
  *
16
- * @param {object} targetObj Object to copy attributes from
17
- * @param {array} attributes Array of keys
18
- * @returns {object} New object with attributes of targetObj
20
+ * @param {object} target - Object to copy attributes from
21
+ * @param {array} attributes - Array of keys
22
+ *
23
+ * @return {object} New object with attributes of target
19
24
  *
20
25
  * @alias copyWithCertainAttributes
21
- * @public
26
+ *
27
+ * @api public
22
28
  */
23
- function _copyWithCertainAttributes(targetObj={}, attributes=[]) {
29
+ function _copyWithCertainAttributes(target={}, attributes=[]) {
30
+ ensure(target, 'object,required', 'target');
31
+ ensure(attributes, 'array,required', 'attributes');
32
+
24
33
  const result = {};
25
34
 
26
- attributes.forEach(a => result[a] = targetObj[a]);
35
+ attributes.forEach(a => result[a] = target[a]);
27
36
 
28
37
  return result;
29
38
  }
30
39
 
31
40
  /**
32
- * Merge the property descriptors of `src` into `dest`
41
+ * Merge the property descriptors of `src` into `target`
33
42
  *
34
- * @param {object} dest Object to add descriptors to
35
- * @param {object} src Object to clone descriptors from
36
- * @param {boolean} [redefine=true] Redefine `dest` properties with `src` properties
37
- * @returns {object} Reference to dest
43
+ * @param {object} target - Object to add descriptors to
44
+ * @param {object} src - Object to clone descriptors from
45
+ * @param {boolean} [redefine=true] - Redefine `target` properties with `src` properties
46
+ *
47
+ * @return {object} Reference to target
38
48
  *
39
49
  * @alias merge
40
- * @public
50
+ *
51
+ * @api public
41
52
  */
42
- function _merge (dest={}, src={}, redefine=true) {
43
- if (!dest) {
44
- throw new TypeError('argument dest is required')
45
- }
46
-
47
- if (!src) {
48
- throw new TypeError('argument src is required')
49
- }
53
+ function _merge (target={}, src={}, redefine=true) {
54
+ ensure(target, 'object,required', 'target');
55
+ ensure(src, 'object,required', 'src');
56
+ ensure(redefine, 'boolean', 'redefine');
50
57
 
51
58
  if (redefine === true) {
52
- dest = Object.assign(dest, src);
59
+ target = Object.assign(target, src);
53
60
  }
54
61
  else {
55
62
  Object.getOwnPropertyNames(src)
56
63
  .forEach(function forEachOwnPropertyName(name) {
57
- if (!redefine && hasOwnProperty.call(dest, name)) {
64
+ if (!redefine && hasOwnProperty.call(target, name)) {
58
65
  // Skip descriptor.
59
66
  return;
60
67
  }
61
68
 
62
69
  // Copy descriptor:
63
70
  const descriptor = Object.getOwnPropertyDescriptor(src, name)
64
- Object.defineProperty(dest, name, descriptor)
71
+ Object.defineProperty(target, name, descriptor)
65
72
  });
66
73
  }
67
74
 
68
- return dest;
75
+ return target;
69
76
  }
@@ -1,3 +1,9 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+ 'use strict';
6
+
1
7
 
2
8
  module.exports = {
3
9
  NUMBER: _NUMBER,
@@ -44,8 +50,8 @@ function _BOOLEAN(value=undefined, options={ fallback:undefined }) {
44
50
  return value;
45
51
 
46
52
  // If string-boolean.
47
- if (typeof value === "string")
48
- return value === "true";
53
+ if (typeof value === 'string')
54
+ return value === 'true';
49
55
 
50
56
  throw new Error(`Not a Boolean`);
51
57
  }
@@ -56,7 +62,7 @@ function _BOOLEAN(value=undefined, options={ fallback:undefined }) {
56
62
 
57
63
  function _STRING(value=undefined, options={ fallback:undefined }) {
58
64
  try {
59
- if (typeof value !== "string")
65
+ if (typeof value !== 'string')
60
66
  throw new Error(`Not a String`);
61
67
 
62
68
  return value;
@@ -97,7 +103,7 @@ function _DATE(value=undefined, options={ fallback:undefined }) {
97
103
 
98
104
  function _JSON(value=undefined, options={ fallback:undefined }) {
99
105
  try {
100
- if (typeof value === "string")
106
+ if (typeof value === 'string')
101
107
  return JSON.parse(value);
102
108
 
103
109
  if (typeof value !== 'object')
@@ -0,0 +1,68 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
8
+
9
+ module.exports = {
10
+ ensure: _ensure
11
+ }
12
+
13
+ /*
14
+ * Validates given argument against rules.
15
+ *
16
+ * @param {Any} argument
17
+ * @param {String} rules
18
+ * @param {String} argumentName
19
+ *
20
+ * @api private
21
+ * @alias ensure
22
+ */
23
+ function _ensure(argument, rules, argumentName) {
24
+ const name = argumentName ? `'${ argumentName }'` : 'Argument';
25
+
26
+ let rulesArray = rules.split(',');
27
+
28
+ let types = [];
29
+ let isRequired = undefined;
30
+
31
+ for (const rule of rulesArray) {
32
+ // Types:
33
+ if (rule.indexOf('|') > 0) {
34
+ types = [
35
+ ...types,
36
+ ...rule.split('|')
37
+ ];
38
+ continue;
39
+ }
40
+
41
+ if (rule === 'required') {
42
+ if (argument === undefined || argument === null) {
43
+ throw new Error(`${ name } is required.`);
44
+ }
45
+
46
+ isRequired = true;
47
+ continue;
48
+ }
49
+
50
+ types.push(rule);
51
+ }
52
+
53
+ if (types.length === 0)
54
+ return true;
55
+
56
+ let mismatchedTypesCount = 0;
57
+ for (const type of types) {
58
+ if (typeof argument !== type) {
59
+ mismatchedTypesCount++;
60
+ }
61
+ }
62
+
63
+ if (mismatchedTypesCount === types.length && argument !== undefined) {
64
+ throw new TypeError(`${ name } must be of type ${ types.join('|') }.`);
65
+ }
66
+
67
+ return true;
68
+ }
@@ -1,3 +1,10 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
 
2
9
  module.exports = {
3
10
  isValidDate: _isValidDate
@@ -1,3 +1,10 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+
6
+ 'use strict';
7
+
1
8
 
2
9
  module.exports = {
3
10
  isValidNumber: _isValidNumber