nodester 0.2.5 → 0.2.8
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.
- package/.eslintrc.js +13 -0
- package/Readme.md +12 -4
- package/lib/application/index.js +61 -53
- package/lib/body/extract.js +10 -10
- package/lib/controllers/methods/index.js +29 -14
- package/lib/controllers/mixins/index.js +13 -13
- package/lib/database/connection.js +78 -17
- package/lib/database/migration.js +9 -3
- package/lib/errors/CustomError.js +7 -2
- package/lib/errors/NodesterError.js +10 -2
- package/lib/errors/NodesterQueryError.js +9 -2
- package/lib/errors/index.js +2 -2
- package/lib/facades/methods/index.js +15 -15
- package/lib/facades/mixins/index.js +12 -12
- package/lib/factories/responses/html.js +20 -9
- package/lib/factories/responses/rest.js +21 -19
- package/lib/http/codes/descriptions.js +4 -3
- package/lib/http/codes/index.js +3 -2
- package/lib/http/codes/symbols.js +3 -2
- package/lib/http/request/index.js +20 -250
- package/lib/http/request/utils.js +4 -4
- package/lib/http/response/headers.js +16 -19
- package/lib/http/response/index.js +25 -28
- package/lib/http/response/utils.js +7 -6
- package/lib/loggers/console.js +3 -4
- package/lib/loggers/dev.js +3 -4
- package/lib/middlewares/404/index.js +38 -0
- package/lib/middlewares/SearchParams/index.js +3 -3
- package/lib/middlewares/cookies/index.js +2 -2
- package/lib/middlewares/etag/index.js +10 -8
- package/lib/middlewares/formidable/index.js +2 -2
- package/lib/middlewares/ql/sequelize/index.js +2 -2
- package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +25 -4
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +30 -18
- package/lib/middlewares/render/index.js +3 -3
- package/lib/models/associate.js +3 -2
- package/lib/models/define.js +37 -18
- package/lib/models/mixins.js +9 -9
- package/lib/query/traverse.js +40 -32
- package/lib/router/handlers.util.js +5 -5
- package/lib/router/index.js +84 -70
- package/lib/router/markers.js +5 -5
- package/lib/router/route.js +18 -19
- package/lib/router/routes.util.js +4 -4
- package/lib/router/utils.js +2 -2
- package/lib/stacks/MarkersStack.js +11 -9
- package/lib/stacks/MiddlewaresStack.js +25 -21
- package/lib/structures/Enum.js +8 -2
- package/lib/structures/Filter.js +31 -29
- package/lib/structures/Params.js +3 -3
- package/lib/tools/nql.tool.js +10 -2
- package/lib/tools/sql.tool.js +19 -2
- package/lib/utils/json.util.js +28 -27
- package/lib/utils/models.js +3 -2
- package/lib/utils/objects.util.js +10 -11
- package/lib/utils/path.util.js +9 -3
- package/lib/utils/sanitizations.util.js +3 -2
- package/lib/utils/types.util.js +11 -4
- package/lib/validators/arguments.js +28 -9
- package/lib/validators/dates.js +4 -5
- package/lib/validators/numbers.js +4 -4
- package/package.json +43 -39
- package/tests/nql.test.js +20 -7
- /package/lib/constants/{Operations.js → Operators.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -14,29 +14,31 @@ const fs = require('fs');
|
|
|
14
14
|
const getFileStats = promisify(fs.stat);
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
module.exports = initETagMiddleware;
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* Initialize `etag` middleware.
|
|
19
21
|
*
|
|
20
|
-
* @param {Object} options
|
|
21
|
-
* @param {
|
|
22
|
+
* @param {Object} [options]
|
|
23
|
+
* @param {boolean} options.weak
|
|
22
24
|
*
|
|
23
25
|
* @return {Function}
|
|
24
26
|
*
|
|
25
|
-
* @
|
|
27
|
+
* @access public
|
|
26
28
|
*/
|
|
27
|
-
|
|
29
|
+
function initETagMiddleware(options) {
|
|
28
30
|
const context = {
|
|
29
31
|
options
|
|
30
32
|
}
|
|
31
33
|
return handle.bind(context);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
/**
|
|
35
37
|
* Add ETag header field.
|
|
36
38
|
*/
|
|
37
39
|
function handle(req, res, next) {
|
|
38
40
|
// console.log('e', req.headers);
|
|
39
|
-
next();
|
|
41
|
+
return next();
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
async function _getResponseEntity(res) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
const debug = require('debug')('nodester:interpreter:ModelsTree');
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @class
|
|
13
|
+
*
|
|
14
|
+
* @access public
|
|
15
|
+
*/
|
|
11
16
|
class ModelsTreeNode {
|
|
12
17
|
constructor(model, parent=null, opts={}) {
|
|
13
18
|
this.model = model;
|
|
@@ -17,7 +22,7 @@ class ModelsTreeNode {
|
|
|
17
22
|
this.fn = null;
|
|
18
23
|
|
|
19
24
|
// for override:
|
|
20
|
-
this.
|
|
25
|
+
this._attributes = [];
|
|
21
26
|
this._where = {};
|
|
22
27
|
this._functions = [];
|
|
23
28
|
this.skip = 0;
|
|
@@ -28,6 +33,11 @@ class ModelsTreeNode {
|
|
|
28
33
|
this.order_by = opts.order_by ?? 'id';
|
|
29
34
|
}
|
|
30
35
|
|
|
36
|
+
// Getters:
|
|
37
|
+
get attributes() {
|
|
38
|
+
return this._attributes;
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
get where() {
|
|
32
42
|
return this._where;
|
|
33
43
|
}
|
|
@@ -51,6 +61,12 @@ class ModelsTreeNode {
|
|
|
51
61
|
get hasIncludes() {
|
|
52
62
|
return this.includesCount > 0;
|
|
53
63
|
}
|
|
64
|
+
// Getters\
|
|
65
|
+
|
|
66
|
+
// Setters:
|
|
67
|
+
set attributes(array) {
|
|
68
|
+
this._attributes = array;
|
|
69
|
+
}
|
|
54
70
|
|
|
55
71
|
resetActiveParam() {
|
|
56
72
|
this.activeParam = null;
|
|
@@ -85,7 +101,7 @@ class ModelsTreeNode {
|
|
|
85
101
|
return {
|
|
86
102
|
model: this.model,
|
|
87
103
|
|
|
88
|
-
|
|
104
|
+
attributes: this.attributes,
|
|
89
105
|
functions: this.functions,
|
|
90
106
|
|
|
91
107
|
where: this.where,
|
|
@@ -101,6 +117,11 @@ class ModelsTreeNode {
|
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
|
|
120
|
+
/**
|
|
121
|
+
* @class
|
|
122
|
+
*
|
|
123
|
+
* @access public
|
|
124
|
+
*/
|
|
104
125
|
class ModelsTree {
|
|
105
126
|
constructor() {
|
|
106
127
|
this.root = new ModelsTreeNode('root', null);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -12,24 +12,29 @@ const util = require('util');
|
|
|
12
12
|
const debug = require('debug')('nodester:interpreter:QueryLexer');
|
|
13
13
|
|
|
14
14
|
const PARAM_TOKENS = new Enum({
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
ATTRIBUTES: Symbol('attributes'),
|
|
16
|
+
|
|
17
17
|
LIMIT: Symbol('limit'),
|
|
18
18
|
ORDER: Symbol('order'),
|
|
19
19
|
ORDER_BY: Symbol('order_by'),
|
|
20
20
|
SKIP: Symbol('skip'),
|
|
21
|
+
|
|
22
|
+
INCLUDES: Symbol('includes'),
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
const OP_TOKENS = new Enum({
|
|
24
26
|
AND: 'and',
|
|
27
|
+
|
|
25
28
|
BETWEEN: 'between',
|
|
26
29
|
NOT_BETWEEN: 'notBetween',
|
|
27
30
|
BETWEEN_MARK: '~',
|
|
31
|
+
|
|
28
32
|
OR: 'or',
|
|
29
|
-
|
|
33
|
+
OR_SHORT: '|',
|
|
30
34
|
XOR: 'xor',
|
|
35
|
+
|
|
31
36
|
NOT: 'not',
|
|
32
|
-
|
|
37
|
+
NOT_SHORT: '!',
|
|
33
38
|
|
|
34
39
|
IN: 'in',
|
|
35
40
|
NOT_IN: 'notIn',
|
|
@@ -49,6 +54,14 @@ const FN_TOKENS = new Enum({
|
|
|
49
54
|
});
|
|
50
55
|
|
|
51
56
|
|
|
57
|
+
/**
|
|
58
|
+
* @class
|
|
59
|
+
* @classdef constructs ModelTree based on the querystring
|
|
60
|
+
*
|
|
61
|
+
* @param {string} queryString
|
|
62
|
+
*
|
|
63
|
+
* @access public
|
|
64
|
+
*/
|
|
52
65
|
module.exports = class QueryLexer {
|
|
53
66
|
constructor(queryString='') {
|
|
54
67
|
this.tree = new ModelsTree();
|
|
@@ -77,7 +90,7 @@ module.exports = class QueryLexer {
|
|
|
77
90
|
|
|
78
91
|
// Token is a String, accumulated char-by-char.
|
|
79
92
|
let token = '';
|
|
80
|
-
// Value of param ('id=10' OR '
|
|
93
|
+
// Value of param ('id=10' OR 'attributes=id,text').
|
|
81
94
|
let value = [];
|
|
82
95
|
// Model, that was active before a cursor went up in the tree.
|
|
83
96
|
let previousActive = null;
|
|
@@ -489,13 +502,16 @@ module.exports = class QueryLexer {
|
|
|
489
502
|
|
|
490
503
|
parseParamFromToken(token) {
|
|
491
504
|
switch(token) {
|
|
505
|
+
case 'attributes':
|
|
506
|
+
case 'a':
|
|
507
|
+
return PARAM_TOKENS.ATTRIBUTES;
|
|
508
|
+
|
|
492
509
|
case 'limit':
|
|
493
510
|
case 'l':
|
|
494
511
|
return PARAM_TOKENS.LIMIT;
|
|
495
512
|
|
|
496
513
|
case 'skip':
|
|
497
514
|
case 's':
|
|
498
|
-
case 'offset':
|
|
499
515
|
return PARAM_TOKENS.SKIP;
|
|
500
516
|
|
|
501
517
|
case 'order':
|
|
@@ -503,13 +519,9 @@ module.exports = class QueryLexer {
|
|
|
503
519
|
return PARAM_TOKENS.ORDER;
|
|
504
520
|
|
|
505
521
|
case 'order_by':
|
|
506
|
-
case '
|
|
522
|
+
case 'oby':
|
|
507
523
|
return PARAM_TOKENS.ORDER_BY;
|
|
508
524
|
|
|
509
|
-
case 'fields':
|
|
510
|
-
case 'f':
|
|
511
|
-
return PARAM_TOKENS.FIELDS;
|
|
512
|
-
|
|
513
525
|
case 'includes':
|
|
514
526
|
case 'in':
|
|
515
527
|
return PARAM_TOKENS.INCLUDES;
|
|
@@ -525,6 +537,11 @@ module.exports = class QueryLexer {
|
|
|
525
537
|
debug(`set param`, { param, token, value });
|
|
526
538
|
|
|
527
539
|
switch(param) {
|
|
540
|
+
case PARAM_TOKENS.ATTRIBUTES:
|
|
541
|
+
if (token) value.push(token);
|
|
542
|
+
treeNode.attributes = value;
|
|
543
|
+
break;
|
|
544
|
+
|
|
528
545
|
case PARAM_TOKENS.LIMIT:
|
|
529
546
|
treeNode.limit = parseInt(token);
|
|
530
547
|
break;
|
|
@@ -541,11 +558,6 @@ module.exports = class QueryLexer {
|
|
|
541
558
|
treeNode.order_by = token;
|
|
542
559
|
break;
|
|
543
560
|
|
|
544
|
-
case PARAM_TOKENS.FIELDS:
|
|
545
|
-
if (token) value.push(token);
|
|
546
|
-
treeNode.fields = value;
|
|
547
|
-
break;
|
|
548
|
-
|
|
549
561
|
case PARAM_TOKENS.INCLUDES:
|
|
550
562
|
const node = new ModelsTreeNode(token);
|
|
551
563
|
treeNode.include(node);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -29,7 +29,7 @@ function handle(req, res, next) {
|
|
|
29
29
|
* - `filename` filename of the view being rendered
|
|
30
30
|
*
|
|
31
31
|
* @alias render
|
|
32
|
-
* @
|
|
32
|
+
* @access public
|
|
33
33
|
*/
|
|
34
34
|
function _render(view, options, callback) {
|
|
35
35
|
const app = this.req.app;
|
package/lib/models/associate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -11,6 +11,7 @@ module.exports = {
|
|
|
11
11
|
associateModelsSync: _associateModelsSync
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
async function _associateModels(databaseConnection) {
|
|
15
16
|
try {
|
|
16
17
|
const models = databaseConnection.models;
|
package/lib/models/define.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -13,13 +13,15 @@ const { DataTypes } = require('sequelize');
|
|
|
13
13
|
|
|
14
14
|
module.exports = defineModel;
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
17
|
* @param {SequilizeConnection} databaseConnection
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {string} modelName
|
|
19
19
|
* @param {Function} definition
|
|
20
|
-
* @param {Object}
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* @param {Object} [options]
|
|
21
|
+
* ... Sequilize model options
|
|
22
|
+
* @param {Object} [options.nodester]
|
|
23
|
+
* @param {boolean} [options.nodester.noCRUD]
|
|
24
|
+
* @param {string} [options.nodester.output]
|
|
23
25
|
*/
|
|
24
26
|
function defineModel(
|
|
25
27
|
databaseConnection,
|
|
@@ -46,16 +48,18 @@ function defineModel(
|
|
|
46
48
|
|
|
47
49
|
// Configs related to nodester:
|
|
48
50
|
nodester: {
|
|
51
|
+
noCRUD: false,
|
|
49
52
|
output: 'underscored'
|
|
50
53
|
},
|
|
51
54
|
|
|
52
|
-
// Add user-defined options
|
|
55
|
+
// Add user-defined options
|
|
56
|
+
// (they can fully override upper ones).
|
|
53
57
|
...options
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
const model = databaseConnection.define(modelName, definitionObject, _options);
|
|
57
61
|
|
|
58
|
-
if (
|
|
62
|
+
if (_options.nodester.noCRUD !== true) {
|
|
59
63
|
// Add:
|
|
60
64
|
// - createWithIncludes;
|
|
61
65
|
// - findById;
|
|
@@ -67,7 +71,7 @@ function defineModel(
|
|
|
67
71
|
|
|
68
72
|
// Associations:
|
|
69
73
|
model.associate = (models) => {};
|
|
70
|
-
model.
|
|
74
|
+
model.getIncludesTree = _getIncludesTree.bind(model);
|
|
71
75
|
|
|
72
76
|
// Instance methods:
|
|
73
77
|
model.prototype.toJSON = function() {
|
|
@@ -78,8 +82,22 @@ function defineModel(
|
|
|
78
82
|
return model;
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
// Associations mixins:
|
|
86
|
+
/**
|
|
87
|
+
* @example
|
|
88
|
+
* [
|
|
89
|
+
* <include_name_0>: {
|
|
90
|
+
* <subinclude_name_0>: { ... }
|
|
91
|
+
* },
|
|
92
|
+
* <include_name_1>: { ... }
|
|
93
|
+
* ]
|
|
94
|
+
* @param {Object|null} data
|
|
95
|
+
*
|
|
96
|
+
* @return {Array} associationsTree
|
|
97
|
+
*
|
|
98
|
+
* @alias getIncludesTree
|
|
99
|
+
*/
|
|
100
|
+
function _getIncludesTree(data=null) {
|
|
83
101
|
const result = [];
|
|
84
102
|
|
|
85
103
|
const associations = this.associations;
|
|
@@ -88,15 +106,16 @@ function _getIncludesList(facadeData=null) {
|
|
|
88
106
|
for (const [ associationName, associationDefinition ] of associationEntries) {
|
|
89
107
|
const formatted = { association: associationName };
|
|
90
108
|
|
|
91
|
-
if (
|
|
92
|
-
// If
|
|
93
|
-
|
|
109
|
+
if (typeof data === 'object') {
|
|
110
|
+
// If data (for example during create)
|
|
111
|
+
// is set, go deeper:
|
|
112
|
+
const keys = Object.keys( data );
|
|
94
113
|
if (keys.indexOf(associationName) > 0) {
|
|
95
114
|
const associationModel = associationDefinition.target;
|
|
96
115
|
|
|
97
116
|
if (Object.entries(associationModel.associations).length > 0) {
|
|
98
|
-
const deepData =
|
|
99
|
-
formatted.include = associationModel.
|
|
117
|
+
const deepData = data[ associationName ];
|
|
118
|
+
formatted.include = associationModel.getIncludesTree(
|
|
100
119
|
Array.isArray(deepData) ? deepData[0] : deepData
|
|
101
120
|
);
|
|
102
121
|
}
|
|
@@ -108,4 +127,4 @@ function _getIncludesList(facadeData=null) {
|
|
|
108
127
|
|
|
109
128
|
return result;
|
|
110
129
|
}
|
|
111
|
-
|
|
130
|
+
// Associations mixins\
|
package/lib/models/mixins.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
9
|
* CRUD mixins for any model:
|
|
10
10
|
*/
|
|
11
|
+
|
|
11
12
|
// Utils:
|
|
12
13
|
const {
|
|
13
14
|
modelHasAssociations,
|
|
@@ -30,8 +31,7 @@ module.exports = {
|
|
|
30
31
|
*
|
|
31
32
|
* @param {Object} modelDefinition
|
|
32
33
|
*
|
|
33
|
-
*
|
|
34
|
-
* @api public
|
|
34
|
+
* @access public
|
|
35
35
|
* @alias implementsCRUD
|
|
36
36
|
*/
|
|
37
37
|
function _implementsCRUD(modelDefinition) {
|
|
@@ -57,7 +57,7 @@ function _implementsCRUD(modelDefinition) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
/** Main mixinis: */
|
|
61
61
|
async function _createWithIncludes(
|
|
62
62
|
data={}
|
|
63
63
|
) {
|
|
@@ -290,9 +290,9 @@ function _deleteById(
|
|
|
290
290
|
};
|
|
291
291
|
return this.destroy(query);
|
|
292
292
|
}
|
|
293
|
-
|
|
293
|
+
/** Main mixinis\ */
|
|
294
294
|
|
|
295
|
-
|
|
295
|
+
/** Subfunctions: */
|
|
296
296
|
async function _updateOrCreateOrDelete(
|
|
297
297
|
modelDefinition,
|
|
298
298
|
data
|
|
@@ -395,4 +395,4 @@ function _unwrapUpdateOrCreateOrDeleteOperationsResults(
|
|
|
395
395
|
|
|
396
396
|
return result;
|
|
397
397
|
}
|
|
398
|
-
|
|
398
|
+
/** Subfunctions\ */
|
package/lib/query/traverse.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -16,6 +16,14 @@ const { ensure } = require('nodester/validators/arguments');
|
|
|
16
16
|
|
|
17
17
|
module.exports = traverse;
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param {ModelsTreeNode} queryNode
|
|
22
|
+
* @param {NodesterFilter} filter
|
|
23
|
+
* @param {Model} model
|
|
24
|
+
*
|
|
25
|
+
* @access public
|
|
26
|
+
*/
|
|
19
27
|
function traverse(queryNode, filter=null, model=null) {
|
|
20
28
|
const _model = model ?? filter.model;
|
|
21
29
|
|
|
@@ -35,7 +43,7 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
35
43
|
const rootModelName = _model.options.name;
|
|
36
44
|
const rootModelAssociations = _model.associations;
|
|
37
45
|
const { sequelize } = _model;
|
|
38
|
-
const
|
|
46
|
+
const attributesAvailable = Object.keys(_model.tableAttributes);
|
|
39
47
|
|
|
40
48
|
const newQuery = {
|
|
41
49
|
attributes: [],
|
|
@@ -44,60 +52,61 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
44
52
|
};
|
|
45
53
|
|
|
46
54
|
const {
|
|
55
|
+
attributes,
|
|
56
|
+
clauses,
|
|
57
|
+
functions,
|
|
47
58
|
where,
|
|
59
|
+
|
|
48
60
|
includes,
|
|
49
|
-
fields,
|
|
50
|
-
functions,
|
|
51
|
-
clauses,
|
|
52
61
|
} = _disassembleQueryNode(queryNode);
|
|
53
62
|
|
|
54
63
|
|
|
55
|
-
//
|
|
64
|
+
// Attribute:
|
|
56
65
|
//
|
|
57
66
|
// If Filter is not set,
|
|
58
|
-
// use every available
|
|
67
|
+
// use every available attribute:
|
|
59
68
|
if (filter === null) {
|
|
60
|
-
for (let
|
|
61
|
-
// If no query filter or
|
|
62
|
-
if (
|
|
63
|
-
newQuery.attributes.push(
|
|
69
|
+
for (let attribute of attributesAvailable) {
|
|
70
|
+
// If no query filter or attribute is requested:
|
|
71
|
+
if (attributes.length === 0 || attributes.indexOf(attribute) > -1) {
|
|
72
|
+
newQuery.attributes.push(attribute);
|
|
64
73
|
continue;
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
76
|
}
|
|
68
77
|
// Filter is present:
|
|
69
78
|
else {
|
|
70
|
-
// If no query
|
|
79
|
+
// If no query attributes were set,
|
|
71
80
|
// use the ones from Filter,
|
|
72
|
-
// If query
|
|
81
|
+
// If query attributes were set,
|
|
73
82
|
// put them through Filter:
|
|
74
|
-
for (let
|
|
75
|
-
if (
|
|
76
|
-
const err = new NodesterQueryError(`Field '${
|
|
83
|
+
for (let attribute of filter.attributes) {
|
|
84
|
+
if (attributesAvailable.indexOf(attribute) === -1) {
|
|
85
|
+
const err = new NodesterQueryError(`Field '${ attribute }' is not present in model.`);
|
|
77
86
|
Error.captureStackTrace(err, traverse);
|
|
78
87
|
throw err;
|
|
79
88
|
}
|
|
80
89
|
|
|
81
|
-
// If
|
|
82
|
-
// if (filter.
|
|
90
|
+
// If attribute is not in available set:
|
|
91
|
+
// if (filter.attributes.indexOf(attribute) === -1) {
|
|
83
92
|
// continue;
|
|
84
93
|
// }
|
|
85
94
|
|
|
86
|
-
// If no query filter or
|
|
87
|
-
if (
|
|
88
|
-
newQuery.attributes.push(
|
|
95
|
+
// If no query filter or attribute is requested:
|
|
96
|
+
if (attributes.length === 0 || attributes.indexOf(attribute) > -1) {
|
|
97
|
+
newQuery.attributes.push(attribute);
|
|
89
98
|
continue;
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
// At least 1
|
|
103
|
+
// At least 1 attribute is mandatory:
|
|
95
104
|
if (newQuery.attributes.length === 0) {
|
|
96
|
-
const err = new NodesterQueryError(`No
|
|
105
|
+
const err = new NodesterQueryError(`No attributes were selected.`);
|
|
97
106
|
Error.captureStackTrace(err, traverse);
|
|
98
107
|
throw err;
|
|
99
108
|
}
|
|
100
|
-
//
|
|
109
|
+
// Attribute\
|
|
101
110
|
|
|
102
111
|
// Functions:
|
|
103
112
|
for (const fnParams of functions) {
|
|
@@ -312,7 +321,7 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
312
321
|
* @param {NodesterFilter} filter
|
|
313
322
|
* @param {Object} resultQuery
|
|
314
323
|
*
|
|
315
|
-
* @
|
|
324
|
+
* @access private
|
|
316
325
|
*/
|
|
317
326
|
function _traverseIncludes(includes, rootModel, filter, resultQuery) {
|
|
318
327
|
const filterIncludesEntries = Object.entries(filter.includes);
|
|
@@ -377,20 +386,19 @@ function _parseWhereEntry(attribute, value, whereHolder, staticAttributes) {
|
|
|
377
386
|
function _disassembleQueryNode(queryNode) {
|
|
378
387
|
// Disassemble current query node:
|
|
379
388
|
const {
|
|
389
|
+
attributes,
|
|
390
|
+
functions,
|
|
380
391
|
where,
|
|
381
392
|
includes,
|
|
382
|
-
fields,
|
|
383
|
-
functions,
|
|
384
393
|
...clauses
|
|
385
394
|
} = queryNode;
|
|
386
|
-
// delete queryNode.model;
|
|
387
395
|
|
|
388
396
|
return {
|
|
397
|
+
attributes: attributes ?? [],
|
|
398
|
+
clauses: clauses ?? [],
|
|
399
|
+
functions: functions ?? [],
|
|
389
400
|
where: where ?? {},
|
|
390
401
|
includes: includes ?? [],
|
|
391
|
-
fields: fields ?? [],
|
|
392
|
-
functions: functions ?? [],
|
|
393
|
-
clauses: clauses ?? []
|
|
394
402
|
};
|
|
395
403
|
}
|
|
396
404
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const { typeOf } = require('
|
|
8
|
+
const { typeOf } = require('nodester/utils/types');
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
module.exports = {
|
|
@@ -13,12 +13,12 @@ module.exports = {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
17
|
* @param {Object} routeHandler
|
|
18
18
|
* @return {Object} parsedRouteHandler
|
|
19
19
|
*
|
|
20
20
|
* @alias parseRouteHandler
|
|
21
|
-
* @public
|
|
21
|
+
* @access public
|
|
22
22
|
*/
|
|
23
23
|
function _parseRouteHandler(routeHandler={}) {
|
|
24
24
|
if (!routeHandler) {
|