nodester 0.0.1 → 0.0.2

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 (68) 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 +61 -0
  30. package/lib/router/index.js +386 -0
  31. package/lib/router/route.js +124 -0
  32. package/lib/router/routes.util.js +66 -0
  33. package/lib/stacks/MarkersStack.js +35 -0
  34. package/lib/{application → stacks}/MiddlewareStack.js +47 -13
  35. package/lib/utils/path.util.js +3 -1
  36. package/lib/utils/types.util.js +51 -1
  37. package/lib/validators/dates.js +25 -0
  38. package/lib/validators/numbers.js +14 -0
  39. package/package.json +31 -4
  40. package/tests/index.test.js +7 -2
  41. package/tests/nql.test.js +277 -0
  42. package/docs/App.md +0 -13
  43. package/docs/Queries.md +0 -61
  44. package/docs/Readme.md +0 -2
  45. package/docs/Routing.md +0 -34
  46. package/examples/goal/index.js +0 -23
  47. package/examples/rest/index.js +0 -25
  48. package/examples/rest/node_modules/.package-lock.json +0 -40
  49. package/examples/rest/package-lock.json +0 -72
  50. package/examples/rest/package.json +0 -14
  51. package/lib/constants/ConstantsEnum.js +0 -13
  52. package/lib/controllers/Controller.js +0 -474
  53. package/lib/controllers/JWTController.js +0 -240
  54. package/lib/controllers/ServiceController.js +0 -109
  55. package/lib/controllers/WebController.js +0 -75
  56. package/lib/facades/Facade.js +0 -388
  57. package/lib/facades/FacadeParams.js +0 -11
  58. package/lib/facades/ServiceFacade.js +0 -17
  59. package/lib/facades/jwt.facade.js +0 -273
  60. package/lib/factories/responses/api.js +0 -90
  61. package/lib/models/DisabledRefreshToken.js +0 -68
  62. package/lib/models/Extractor.js +0 -320
  63. package/lib/routers/Default/index.js +0 -143
  64. package/lib/routers/Default/layer.js +0 -50
  65. package/lib/routers/Main/index.js +0 -10
  66. package/lib/routers/Roles/index.js +0 -81
  67. package/lib/utils/params.util.js +0 -19
  68. /package/lib/{application/http → http}/utils.js +0 -0
@@ -0,0 +1,311 @@
1
+ const { Op } = require('sequelize');
2
+
3
+
4
+ module.exports = traverse;
5
+
6
+ function traverse(queryNode, colander=null, model) {
7
+
8
+ const fieldsAvailable = Object.keys(model.tableAttributes);
9
+ const includesAvailable = model.getIncludesList();
10
+
11
+ const newQuery = {
12
+ attributes: [],
13
+ where: {},
14
+ include: []
15
+ };
16
+
17
+ const {
18
+ where,
19
+ includes,
20
+ fields,
21
+ clauses,
22
+ } = _disassembleQueryNode(queryNode);
23
+
24
+
25
+ // Fields:
26
+ //
27
+ // If Colander is not set,
28
+ // use every available field:
29
+ if (colander === null) {
30
+ for (let field of fieldsAvailable) {
31
+ // If no query filter or field is requested:
32
+ if (fields.length === 0 || fields.indexOf(field) > -1) {
33
+ newQuery.attributes.push(field);
34
+ continue;
35
+ }
36
+ }
37
+ }
38
+ // Colander is present:
39
+ else {
40
+ // If no query fields were set,
41
+ // use the ones from Colander,
42
+ // If query fields were set,
43
+ // put them through Colander:
44
+ for (let field of colander.fields) {
45
+ if (fieldsAvailable.indexOf(field) === -1) {
46
+ const err = new TypeError(`field ${ field } is not present in model.`);
47
+ throw err;
48
+ }
49
+
50
+ // If field is not in available set:
51
+ // if (colander.fields.indexOf(field) === -1) {
52
+ // continue;
53
+ // }
54
+
55
+ // If no query filter or field is requested:
56
+ if (fields.length === 0 || fields.indexOf(field) > -1) {
57
+ newQuery.attributes.push(field);
58
+ continue;
59
+ }
60
+ }
61
+ }
62
+
63
+ // At least 1 field is mandatory:
64
+ if (newQuery.attributes.length === 0) {
65
+ const err = new TypeError(`No fields were selected.`);
66
+ throw err;
67
+ }
68
+ // Fields\
69
+
70
+
71
+ // Clauses:
72
+ const order = {};
73
+
74
+ const clausesEntries = Object.entries(clauses);
75
+ for (let [clauseName, value] of clausesEntries) {
76
+ // If clause is not available:
77
+ if (colander != null) {
78
+ if (colander.clauses.indexOf(clauseName) === -1)
79
+ continue;
80
+ }
81
+
82
+ switch(clauseName) {
83
+ case 'limit':
84
+ // Do not set if -1:
85
+ if (value === -1)
86
+ continue;
87
+
88
+ newQuery.limit = value;
89
+ continue;
90
+ case 'skip':
91
+ // Do not set if 0:
92
+ if (value === 0)
93
+ continue;
94
+
95
+ newQuery.offset = value;
96
+ continue;
97
+ case 'order':
98
+ order.order = value;
99
+ continue;
100
+ case 'order_by':
101
+ order.by = value;
102
+ continue;
103
+ default:
104
+ continue;
105
+ }
106
+ }
107
+
108
+ // "statics" override or set any query Clause:
109
+ if (colander !== null) {
110
+ for (let [clauseName, staticClauseValue] of Object.entries(colander.statics.clauses)) {
111
+ switch(clauseName) {
112
+ case 'limit':
113
+ newQuery.limit = staticClauseValue;
114
+ continue;
115
+ case 'skip':
116
+ newQuery.offset = staticClauseValue;
117
+ continue;
118
+ case 'order':
119
+ order.order = staticClauseValue;
120
+ continue;
121
+ case 'order_by':
122
+ order.by = staticClauseValue;
123
+ continue;
124
+ default:
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ // Clauses\
130
+
131
+
132
+ // Order:
133
+ const sequelize = model.sequelize;
134
+ if ( ['rand', 'random'].indexOf(order.order) > -1) {
135
+ newQuery.order = sequelize.random();
136
+ }
137
+ else {
138
+ const column = sequelize.col( order.by );
139
+ switch (order.order) {
140
+ // MAX/MIN:
141
+ case 'max-asc':
142
+ case 'max':
143
+ case 'min-desc':
144
+ newQuery.order = sequelize.fn('max', column);
145
+ break;
146
+ case 'min':
147
+ case 'min-asc':
148
+ case 'max-desc':
149
+ newQuery.order = [ sequelize.fn('max', column), 'DESC' ];
150
+ break;
151
+ // MAX/MIN\
152
+
153
+ default:
154
+ newQuery.order = [ [order.by, order.order] ];
155
+ break;
156
+ }
157
+ }
158
+ // Order\
159
+
160
+
161
+ // Includes:
162
+ // If requested includes are not available:
163
+ const leftIncludes = includesAvailable.map(i => i.association);
164
+ for (let include of includes) {
165
+ const includeName = include.model;
166
+
167
+ const includeIndex = leftIncludes.indexOf(includeName);
168
+ if (includeIndex === -1) {
169
+ const err = new TypeError(`No include named ${ includeName }`);
170
+ throw err;
171
+ }
172
+
173
+ leftIncludes.splice(includeIndex, 1);
174
+ }
175
+
176
+ _traverseIncludes(includes, model, colander, newQuery)
177
+ // Includes\
178
+
179
+
180
+ // Where:
181
+ const whereEntries = Object.entries(where);
182
+ for (let [attribute, value] of whereEntries) {
183
+ _parseWhereEntry(attribute, value, newQuery.where, colander.statics.attributes);
184
+ }
185
+
186
+ // If "where" was not set:
187
+ if (whereEntries.length === 0) {
188
+ delete newQuery.where;
189
+ }
190
+ // Where\
191
+
192
+
193
+ return newQuery;
194
+ }
195
+
196
+
197
+ function _traverseIncludes(includes, model, colander, resultQuery) {
198
+ // If no Colander:
199
+ if (colander === null) {
200
+ for (let include of includes) {
201
+ const includeName = include.model;
202
+ const association = model.associations[includeName];
203
+
204
+ // If no such association:
205
+ if (!association) {
206
+ const err = new TypeError(`No include ${ includeName }`);
207
+ throw err;
208
+ }
209
+
210
+ const includeModel = association.target;
211
+ // Build query for this include.
212
+ const associationQuery = traverse(include, null, includeModel);
213
+
214
+ _addAssociationQuery(associationQuery, includeName, resultQuery);
215
+ }
216
+ }
217
+ // Colander is present:
218
+ else {
219
+ const colanderIncludeEntries = Object.entries(colander.includes);
220
+ for (let [includeName, includeColander] of colanderIncludeEntries) {
221
+ const association = model.associations[includeName];
222
+ // If no such association:
223
+ if (!association) {
224
+ const err = new TypeError(`No include ${ includeName }`);
225
+ throw err;
226
+ }
227
+
228
+ // If include was not requested:
229
+ const include = includes.find(({ model }) => model === includeName);
230
+ if (!include)
231
+ continue;
232
+
233
+ const includeModel = association.target;
234
+ // Build query for this include.
235
+ const associationQuery = traverse(include, colander.includes[includeName], includeModel);
236
+
237
+ _addAssociationQuery(associationQuery, includeName, resultQuery);
238
+ }
239
+ }
240
+ }
241
+
242
+
243
+ function _addAssociationQuery(associationQuery, includeName, resultQuery) {
244
+
245
+ // Add all association info into query.
246
+ resultQuery.include.push({
247
+ association: includeName,
248
+ ...associationQuery
249
+ });
250
+ }
251
+
252
+
253
+ function _parseWhereEntry(attribute, value, whereHolder, staticAttributes) {
254
+ let _value = value;
255
+ const static = staticAttributes[attribute];
256
+
257
+ // If attribute is Op (like, or, not, etc.):
258
+ if (attribute in Op) {
259
+ // Parse value:
260
+ _value = _parseValue(_value, attribute);
261
+
262
+ const op = Op[attribute];
263
+ whereHolder[op] = _value;
264
+ return;
265
+ }
266
+
267
+ // Static value overrides any other:
268
+ if (!!static) {
269
+ whereHolder[attribute] = static;
270
+ return;
271
+ }
272
+
273
+ whereHolder[attribute] = _parseValue(_value, attribute);
274
+ }
275
+
276
+ function _disassembleQueryNode(queryNode) {
277
+ // Disassemble current query node:
278
+ const {
279
+ where,
280
+ includes,
281
+ fields,
282
+ ...clauses
283
+ } = queryNode;
284
+ // delete queryNode.model;
285
+
286
+ return {
287
+ where: where ?? {},
288
+ includes: includes ?? [],
289
+ fields: fields ?? [],
290
+ clauses: clauses ?? []
291
+ };
292
+ }
293
+
294
+ function _parseValue(value, attribute) {
295
+ // If value is Object:
296
+ if (typeof value === 'object' && Array.isArray(value) === false) {
297
+ const [opKey, rawValue] = (Object.entries(value))[0];
298
+
299
+ // If operation is "in":
300
+ if (opKey === 'in') {
301
+ // Unwrap rawValue.
302
+ return rawValue[0][attribute];
303
+ }
304
+ else {
305
+ const op = Op[opKey];
306
+ return { [op]: rawValue };
307
+ }
308
+ }
309
+
310
+ return value;
311
+ }
@@ -0,0 +1,61 @@
1
+ /*!
2
+ * /nodester
3
+ * MIT Licensed
4
+ */
5
+ 'use strict';
6
+
7
+ const { typeOf } = require('../utils/types.util');
8
+
9
+
10
+ module.exports = {
11
+ parseRouteHandler: _parseRouteHandler
12
+ }
13
+
14
+
15
+ /*
16
+ * @param {Object} routeHandler
17
+ * @return {Object} parsedRouteHandler
18
+ *
19
+ * @alias parseRouteHandler
20
+ * @public
21
+ */
22
+ function _parseRouteHandler(routeHandler={}) {
23
+ if (!routeHandler) {
24
+ const err = new TypeError(`"routeHandler" must be defined.`);
25
+ throw err;
26
+ }
27
+
28
+ const result = {
29
+ before: null,
30
+ controllerName: undefined,
31
+ actionName: undefined,
32
+ };
33
+
34
+ const {
35
+ before,
36
+
37
+ controller,
38
+ controlledBy,
39
+ action
40
+ } = routeHandler;
41
+
42
+ if (!!controlledBy) {
43
+ const parts = controlledBy.split('.');
44
+ const controllerName = parts[0];
45
+ const actionName = parts[1];
46
+ result.controllerName = controllerName;
47
+ result.actionName = actionName;
48
+ }
49
+ else if (!!controller) {
50
+ result.controllerName = `${ controller }`;
51
+ }
52
+ else if (!!action) {
53
+ result.actionName = `${ action }`;
54
+ }
55
+
56
+ if (typeOf(before) === 'function') {
57
+ result.before = before;
58
+ }
59
+
60
+ return result;
61
+ }