nodester 0.1.0 → 0.1.5
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/Readme.md +3 -2
- package/lib/application/index.js +1 -1
- package/lib/body/extract.js +78 -0
- package/lib/constants/ErrorCodes.js +19 -0
- package/lib/constants/Operations.js +1 -1
- package/lib/controllers/mixins/index.js +59 -20
- package/lib/facades/methods/index.js +16 -10
- package/lib/factories/errors/CustomError.js +7 -0
- package/lib/factories/errors/NodesterQueryError.js +23 -0
- package/lib/factories/errors/index.js +10 -3
- package/lib/{queries/Colander.js → filters/Filter.js} +37 -12
- package/lib/http/codes/descriptions.js +82 -0
- package/lib/http/codes/index.js +70 -145
- package/lib/http/codes/symbols.js +82 -0
- package/lib/loggers/dev.js +28 -0
- package/lib/middlewares/ql/sequelize/index.js +2 -2
- package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +24 -1
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +67 -15
- package/lib/models/define.js +7 -4
- package/lib/models/mixins.js +1 -1
- package/lib/{queries → query}/traverse.js +108 -30
- package/lib/router/index.js +3 -3
- package/lib/router/routes.util.js +5 -1
- package/lib/stacks/MarkersStack.js +1 -1
- package/lib/stacks/MiddlewareStack.js +1 -1
- package/lib/utils/models.js +14 -0
- package/package.json +28 -4
- package/lib/preprocessors/BodyPreprocessor.js +0 -61
- package/lib/preprocessors/IncludesPreprocessor.js +0 -55
- package/lib/preprocessors/QueryPreprocessor.js +0 -64
- package/lib/queries/NodesterQueryParams.js +0 -139
- /package/lib/{logger → loggers}/console.js +0 -0
package/lib/http/codes/index.js
CHANGED
|
@@ -1,157 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
EARLY_HINTS: Symbol('103: Early Hints'),
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
OK: Symbol('200: OK'),
|
|
10
|
-
CREATED: Symbol('201: Created'),
|
|
11
|
-
ACCEPTED: Symbol('202: Accepted'),
|
|
12
|
-
NON_AUTHORITATIVE_INFORMATION: Symbol('203: Non-Authoritative Information'),
|
|
13
|
-
NO_CONTENT: Symbol('204: No Content'),
|
|
14
|
-
RESET_CONTENT: Symbol('205: Reset Content'),
|
|
15
|
-
PARTIAL_CONTENT: Symbol('206: Partial Content'),
|
|
16
|
-
MULTI_STATUS: Symbol('207: Multi-Status'),
|
|
17
|
-
ALREADY_REPORTED: Symbol('208: Already Reported'),
|
|
18
|
-
IM_USED: Symbol('226: IM Used'),
|
|
7
|
+
const Enum = require('nodester/enum');
|
|
19
8
|
|
|
20
|
-
// Redirections:
|
|
21
|
-
MULTIPLE_CHOICES: Symbol('300: Multiple Choices'),
|
|
22
|
-
MOVED_PERMANENTLY: Symbol('301: Moved Permanently'),
|
|
23
|
-
FOUND: Symbol('302: Found'),
|
|
24
|
-
SEE_OTHER: Symbol('303: See Other'),
|
|
25
|
-
NOT_MODIFIED: Symbol('304: Not Modified'),
|
|
26
|
-
USE_PROXY: Symbol('305: Use Proxy'),
|
|
27
|
-
TEMPORARY_REDIRECT: Symbol('307: Temporary Redirect'),
|
|
28
|
-
PERMANENT_REDIRECT: Symbol('308: Permanent Redirect'),
|
|
29
|
-
|
|
30
|
-
// Client Errors:
|
|
31
|
-
BAD_REQUEST: Symbol('400: Bad Request'),
|
|
32
|
-
UNAUTHORIZED: Symbol('401: Unauthorized'),
|
|
33
|
-
PAYMENT_REQUIRED: Symbol('402: Payment Required'),
|
|
34
|
-
FORBIDDEN: Symbol('403: Forbidden'),
|
|
35
|
-
NOT_FOUND: Symbol('404: Not Found'),
|
|
36
|
-
METHOD_NOT_ALLOWED: Symbol('405: Method Not Allowed'),
|
|
37
|
-
NOT_ACCEPTABLE: Symbol('406: Not Acceptable'),
|
|
38
|
-
PROXY_AUTHENTICATION_REQUIRED: Symbol('407: Proxy Authentication Required'),
|
|
39
|
-
REQUEST_TIMEOUT: Symbol('408: Request Timeout'),
|
|
40
|
-
CONFLICT: Symbol('409: Conflict'),
|
|
41
|
-
GONE: Symbol('410: Gone'),
|
|
42
|
-
LENGTH_REQUIRED: Symbol('411: Length Required'),
|
|
43
|
-
PRECONDITION_FAILED: Symbol('412: Precondition Failed'),
|
|
44
|
-
PAYLOAD_TOO_LARGE: Symbol('413: Payload Too Large'),
|
|
45
|
-
URI_TOO_LONG: Symbol('414: URI Too Long'),
|
|
46
|
-
UNSUPPORTED_MEDIA_TYPE: Symbol('415: Unsupported Media Type'),
|
|
47
|
-
RANGE_NOT_SATISFIABLE: Symbol('416: Range Not Satisfiable'),
|
|
48
|
-
EXPECTATION_FAILED: Symbol('417: Expectation Failed'),
|
|
49
|
-
IM_A_TEAPOT: Symbol("418: I'm a teapot"),
|
|
50
|
-
MISDIRECTED_REQUEST: Symbol('421: Misdirected Request'),
|
|
51
|
-
UNPROCESSABLE_ENTITY: Symbol('422: Unprocessable Entity'),
|
|
52
|
-
LOCKED: Symbol('423: Locked'),
|
|
53
|
-
FAILED_DEPENDENCY: Symbol('424: Failed Dependency'),
|
|
54
|
-
TOO_EARLY: Symbol('425: Too Early'),
|
|
55
|
-
UPGRADE_REQUIRED: Symbol('426: Upgrade Required'),
|
|
56
|
-
PRECONDITION_REQUIRED: Symbol('428: Precondition Required'),
|
|
57
|
-
TOO_MANY_REQUESTS: Symbol('429: Too Many Requests'),
|
|
58
|
-
REQUEST_HEADER_FIELDS_TOO_LARGE: Symbol('431: Request Header Fields Too Large'),
|
|
59
|
-
UNAVAILABLE_FOR_LEGAL_REASONS: Symbol('451: Unavailable For Legal Reasons'),
|
|
60
9
|
|
|
61
|
-
|
|
62
|
-
INTERNAL_SERVER_ERROR: Symbol('500: Internal Server Error'),
|
|
63
|
-
NOT_IMPLEMENTED: Symbol('501: Not Implemented'),
|
|
64
|
-
BAD_GATEWAY: Symbol('502: Bad Gateway'),
|
|
65
|
-
SERVICE_UNAVAILABLE: Symbol('503: Service Unavailable'),
|
|
66
|
-
GATEWAY_TIMEOUT: Symbol('504: Gateway Timeout'),
|
|
67
|
-
HTTP_VERSION_NOT_SUPPORTED: Symbol('505: HTTP Version Not Supported'),
|
|
68
|
-
VARIANT_ALSO_NEGOTIATES: Symbol('506: Variant Also Negotiates'),
|
|
69
|
-
INSUFFICIENT_STORAGE: Symbol('507: Insufficient Storage'),
|
|
70
|
-
LOOP_DETECTED: Symbol('508: Loop Detected'),
|
|
71
|
-
NOT_EXTENDED: Symbol('510: Not Extended'),
|
|
72
|
-
NETWORK_AUTHENTICATION_REQUIRED: Symbol('511: Network Authentication Required')
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const descriptionsByCode = {
|
|
10
|
+
module.exports = new Enum({
|
|
76
11
|
// Informational:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
12
|
+
CONTINUE: 100,
|
|
13
|
+
SWITCHING_PROTOCOLS: 101,
|
|
14
|
+
PROCESSING: 102,
|
|
15
|
+
EARLY_HINTS: 103,
|
|
81
16
|
|
|
82
17
|
// Success:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
18
|
+
OK: 200,
|
|
19
|
+
CREATED: 201,
|
|
20
|
+
ACCEPTED: 202,
|
|
21
|
+
NON_AUTHORITATIVE_INFORMATION: 203,
|
|
22
|
+
NO_CONTENT: 204,
|
|
23
|
+
RESET_CONTENT: 205,
|
|
24
|
+
PARTIAL_CONTENT: 206,
|
|
25
|
+
MULTI_STATUS: 207,
|
|
26
|
+
ALREADY_REPORTED: 208,
|
|
27
|
+
IM_USED: 226,
|
|
93
28
|
|
|
94
29
|
// Redirections:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
30
|
+
MULTIPLE_CHOICES: 300,
|
|
31
|
+
MOVED_PERMANENTLY: 301,
|
|
32
|
+
FOUND: 302,
|
|
33
|
+
SEE_OTHER: 303,
|
|
34
|
+
NOT_MODIFIED: 304,
|
|
35
|
+
USE_PROXY: 305,
|
|
36
|
+
TEMPORARY_REDIRECT: 307,
|
|
37
|
+
PERMANENT_REDIRECT: 308,
|
|
103
38
|
|
|
104
39
|
// Client Errors:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
40
|
+
BAD_REQUEST: 400,
|
|
41
|
+
UNAUTHORIZED: 401,
|
|
42
|
+
PAYMENT_REQUIRED: 402,
|
|
43
|
+
FORBIDDEN: 403,
|
|
44
|
+
NOT_FOUND: 404,
|
|
45
|
+
METHOD_NOT_ALLOWED: 405,
|
|
46
|
+
NOT_ACCEPTABLE: 406,
|
|
47
|
+
PROXY_AUTHENTICATION_REQUIRED: 407,
|
|
48
|
+
REQUEST_TIMEOUT: 408,
|
|
49
|
+
CONFLICT: 409,
|
|
50
|
+
GONE: 410,
|
|
51
|
+
LENGTH_REQUIRED: 411,
|
|
52
|
+
PRECONDITION_FAILED: 412,
|
|
53
|
+
PAYLOAD_TOO_LARGE: 413,
|
|
54
|
+
URI_TOO_LONG: 414,
|
|
55
|
+
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
56
|
+
RANGE_NOT_SATISFIABLE: 416,
|
|
57
|
+
EXPECTATION_FAILED: 417,
|
|
58
|
+
IM_A_TEAPOT: 418,
|
|
59
|
+
MISDIRECTED_REQUEST: 421,
|
|
60
|
+
UNPROCESSABLE_ENTITY: 422,
|
|
61
|
+
LOCKED: 423,
|
|
62
|
+
FAILED_DEPENDENCY: 424,
|
|
63
|
+
TOO_EARLY: 425,
|
|
64
|
+
UPGRADE_REQUIRED: 426,
|
|
65
|
+
PRECONDITION_REQUIRED: 428,
|
|
66
|
+
TOO_MANY_REQUESTS: 429,
|
|
67
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
|
|
68
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: 451,
|
|
134
69
|
|
|
135
70
|
// Server Errors:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
module.exports = {
|
|
150
|
-
descriptions: {
|
|
151
|
-
byCode: descriptionsByCode
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
symbols: {
|
|
155
|
-
byTag: symbolsByTag
|
|
156
|
-
}
|
|
157
|
-
}
|
|
71
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
72
|
+
NOT_IMPLEMENTED: 501,
|
|
73
|
+
BAD_GATEWAY: 502,
|
|
74
|
+
SERVICE_UNAVAILABLE: 503,
|
|
75
|
+
GATEWAY_TIMEOUT: 504,
|
|
76
|
+
HTTP_VERSION_NOT_SUPPORTED: 505,
|
|
77
|
+
VARIANT_ALSO_NEGOTIATES: 506,
|
|
78
|
+
INSUFFICIENT_STORAGE: 507,
|
|
79
|
+
LOOP_DETECTED: 508,
|
|
80
|
+
NOT_EXTENDED: 510,
|
|
81
|
+
NETWORK_AUTHENTICATION_REQUIRED: 511,
|
|
82
|
+
});
|
|
@@ -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
|
+
CONTINUE: Symbol('100: Continue'),
|
|
13
|
+
SWITCHING_PROTOCOLS: Symbol('101: Switching Protocols'),
|
|
14
|
+
PROCESSING: Symbol('102: Processing'),
|
|
15
|
+
EARLY_HINTS: Symbol('103: Early Hints'),
|
|
16
|
+
|
|
17
|
+
// Success:
|
|
18
|
+
OK: Symbol('200: OK'),
|
|
19
|
+
CREATED: Symbol('201: Created'),
|
|
20
|
+
ACCEPTED: Symbol('202: Accepted'),
|
|
21
|
+
NON_AUTHORITATIVE_INFORMATION: Symbol('203: Non-Authoritative Information'),
|
|
22
|
+
NO_CONTENT: Symbol('204: No Content'),
|
|
23
|
+
RESET_CONTENT: Symbol('205: Reset Content'),
|
|
24
|
+
PARTIAL_CONTENT: Symbol('206: Partial Content'),
|
|
25
|
+
MULTI_STATUS: Symbol('207: Multi-Status'),
|
|
26
|
+
ALREADY_REPORTED: Symbol('208: Already Reported'),
|
|
27
|
+
IM_USED: Symbol('226: IM Used'),
|
|
28
|
+
|
|
29
|
+
// Redirections:
|
|
30
|
+
MULTIPLE_CHOICES: Symbol('300: Multiple Choices'),
|
|
31
|
+
MOVED_PERMANENTLY: Symbol('301: Moved Permanently'),
|
|
32
|
+
FOUND: Symbol('302: Found'),
|
|
33
|
+
SEE_OTHER: Symbol('303: See Other'),
|
|
34
|
+
NOT_MODIFIED: Symbol('304: Not Modified'),
|
|
35
|
+
USE_PROXY: Symbol('305: Use Proxy'),
|
|
36
|
+
TEMPORARY_REDIRECT: Symbol('307: Temporary Redirect'),
|
|
37
|
+
PERMANENT_REDIRECT: Symbol('308: Permanent Redirect'),
|
|
38
|
+
|
|
39
|
+
// Client Errors:
|
|
40
|
+
BAD_REQUEST: Symbol('400: Bad Request'),
|
|
41
|
+
UNAUTHORIZED: Symbol('401: Unauthorized'),
|
|
42
|
+
PAYMENT_REQUIRED: Symbol('402: Payment Required'),
|
|
43
|
+
FORBIDDEN: Symbol('403: Forbidden'),
|
|
44
|
+
NOT_FOUND: Symbol('404: Not Found'),
|
|
45
|
+
METHOD_NOT_ALLOWED: Symbol('405: Method Not Allowed'),
|
|
46
|
+
NOT_ACCEPTABLE: Symbol('406: Not Acceptable'),
|
|
47
|
+
PROXY_AUTHENTICATION_REQUIRED: Symbol('407: Proxy Authentication Required'),
|
|
48
|
+
REQUEST_TIMEOUT: Symbol('408: Request Timeout'),
|
|
49
|
+
CONFLICT: Symbol('409: Conflict'),
|
|
50
|
+
GONE: Symbol('410: Gone'),
|
|
51
|
+
LENGTH_REQUIRED: Symbol('411: Length Required'),
|
|
52
|
+
PRECONDITION_FAILED: Symbol('412: Precondition Failed'),
|
|
53
|
+
PAYLOAD_TOO_LARGE: Symbol('413: Payload Too Large'),
|
|
54
|
+
URI_TOO_LONG: Symbol('414: URI Too Long'),
|
|
55
|
+
UNSUPPORTED_MEDIA_TYPE: Symbol('415: Unsupported Media Type'),
|
|
56
|
+
RANGE_NOT_SATISFIABLE: Symbol('416: Range Not Satisfiable'),
|
|
57
|
+
EXPECTATION_FAILED: Symbol('417: Expectation Failed'),
|
|
58
|
+
IM_A_TEAPOT: Symbol("418: I'm a teapot"),
|
|
59
|
+
MISDIRECTED_REQUEST: Symbol('421: Misdirected Request'),
|
|
60
|
+
UNPROCESSABLE_ENTITY: Symbol('422: Unprocessable Entity'),
|
|
61
|
+
LOCKED: Symbol('423: Locked'),
|
|
62
|
+
FAILED_DEPENDENCY: Symbol('424: Failed Dependency'),
|
|
63
|
+
TOO_EARLY: Symbol('425: Too Early'),
|
|
64
|
+
UPGRADE_REQUIRED: Symbol('426: Upgrade Required'),
|
|
65
|
+
PRECONDITION_REQUIRED: Symbol('428: Precondition Required'),
|
|
66
|
+
TOO_MANY_REQUESTS: Symbol('429: Too Many Requests'),
|
|
67
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: Symbol('431: Request Header Fields Too Large'),
|
|
68
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: Symbol('451: Unavailable For Legal Reasons'),
|
|
69
|
+
|
|
70
|
+
// Server Errors:
|
|
71
|
+
INTERNAL_SERVER_ERROR: Symbol('500: Internal Server Error'),
|
|
72
|
+
NOT_IMPLEMENTED: Symbol('501: Not Implemented'),
|
|
73
|
+
BAD_GATEWAY: Symbol('502: Bad Gateway'),
|
|
74
|
+
SERVICE_UNAVAILABLE: Symbol('503: Service Unavailable'),
|
|
75
|
+
GATEWAY_TIMEOUT: Symbol('504: Gateway Timeout'),
|
|
76
|
+
HTTP_VERSION_NOT_SUPPORTED: Symbol('505: HTTP Version Not Supported'),
|
|
77
|
+
VARIANT_ALSO_NEGOTIATES: Symbol('506: Variant Also Negotiates'),
|
|
78
|
+
INSUFFICIENT_STORAGE: Symbol('507: Insufficient Storage'),
|
|
79
|
+
LOOP_DETECTED: Symbol('508: Loop Detected'),
|
|
80
|
+
NOT_EXTENDED: Symbol('510: Not Extended'),
|
|
81
|
+
NETWORK_AUTHENTICATION_REQUIRED: Symbol('511: Network Authentication Required')
|
|
82
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
exports = module.exports = {
|
|
10
|
+
error: _error
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Log error using console.error.
|
|
15
|
+
*
|
|
16
|
+
* @param {Array} args
|
|
17
|
+
*
|
|
18
|
+
* @alias error
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
function _error(...args) {
|
|
23
|
+
const activeEnv = process.env.NODE_ENV
|
|
24
|
+
|
|
25
|
+
if (activeEnv === 'development' || activeEnv === 'testing') {
|
|
26
|
+
console.error(...args);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const QueryLexer = require('./interpreter/QueryLexer');
|
|
2
|
+
const httpCodes = require('nodester/http/codes');
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
module.exports = NodesterQL;
|
|
@@ -18,7 +19,6 @@ async function NodesterQL(req, res, next) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
try {
|
|
21
|
-
// Convert to URLSearchParams.
|
|
22
22
|
const queryString = req.url.split('?')[1];
|
|
23
23
|
|
|
24
24
|
const lexer = new QueryLexer(queryString);
|
|
@@ -28,7 +28,7 @@ async function NodesterQL(req, res, next) {
|
|
|
28
28
|
next();
|
|
29
29
|
}
|
|
30
30
|
catch(error) {
|
|
31
|
-
res.status(
|
|
31
|
+
res.status(error.status ?? httpCodes.UNPROCESSABLE_ENTITY);
|
|
32
32
|
res.json({ error: error.toString() });
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
1
7
|
const debug = require('debug')('nodester:interpreter:ModelsTree');
|
|
2
8
|
|
|
3
9
|
|
|
@@ -7,10 +13,12 @@ class ModelsTreeNode {
|
|
|
7
13
|
this.parent = parent;
|
|
8
14
|
this.activeParam = null;
|
|
9
15
|
this.op = null;
|
|
16
|
+
this.fn = null;
|
|
10
17
|
|
|
11
18
|
// for override:
|
|
12
19
|
this.fields = [];
|
|
13
20
|
this._where = {};
|
|
21
|
+
this._functions = [];
|
|
14
22
|
this.skip = 0;
|
|
15
23
|
this.limit = -1; // No limit
|
|
16
24
|
|
|
@@ -35,6 +43,10 @@ class ModelsTreeNode {
|
|
|
35
43
|
return this._where;
|
|
36
44
|
}
|
|
37
45
|
|
|
46
|
+
get functions() {
|
|
47
|
+
return this._functions;
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
resetActiveParam() {
|
|
39
51
|
this.activeParam = null;
|
|
40
52
|
}
|
|
@@ -43,6 +55,10 @@ class ModelsTreeNode {
|
|
|
43
55
|
this.op = null;
|
|
44
56
|
}
|
|
45
57
|
|
|
58
|
+
resetFN() {
|
|
59
|
+
this.fn = null;
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
addWhere(condition={}) {
|
|
47
63
|
this._where = {
|
|
48
64
|
...this.where,
|
|
@@ -50,6 +66,10 @@ class ModelsTreeNode {
|
|
|
50
66
|
}
|
|
51
67
|
}
|
|
52
68
|
|
|
69
|
+
addFunction(fnParams={}) {
|
|
70
|
+
this._functions.push(fnParams);
|
|
71
|
+
}
|
|
72
|
+
|
|
53
73
|
include(modelTreeNode) {
|
|
54
74
|
modelTreeNode.parent = this;
|
|
55
75
|
this.includes.push(modelTreeNode);
|
|
@@ -60,13 +80,16 @@ class ModelsTreeNode {
|
|
|
60
80
|
return {
|
|
61
81
|
model: this.model,
|
|
62
82
|
|
|
83
|
+
fields: this.fields,
|
|
84
|
+
functions: this.functions,
|
|
85
|
+
|
|
63
86
|
where: this.where,
|
|
87
|
+
|
|
64
88
|
skip: this.skip,
|
|
65
89
|
limit: this.limit,
|
|
66
90
|
order: this.order,
|
|
67
91
|
order_by: this.order_by,
|
|
68
92
|
|
|
69
|
-
fields: this.fields,
|
|
70
93
|
|
|
71
94
|
includes: this.includes.map(i => i.toObject())
|
|
72
95
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const Enum = require('nodester/enum');
|
|
2
9
|
const { ModelsTree, ModelsTreeNode } = require('./ModelsTree');
|
|
3
10
|
const util = require('util');
|
|
4
11
|
const debug = require('debug')('nodester:interpreter:QueryLexer');
|
|
@@ -21,6 +28,10 @@ const OP_TOKENS = new Enum({
|
|
|
21
28
|
LOWER_OR_EQUAL: 'lte'
|
|
22
29
|
});
|
|
23
30
|
|
|
31
|
+
const FN_TOKENS = new Enum({
|
|
32
|
+
COUNT: 'count',
|
|
33
|
+
});
|
|
34
|
+
|
|
24
35
|
|
|
25
36
|
module.exports = class QueryLexer {
|
|
26
37
|
constructor(queryString='') {
|
|
@@ -49,7 +60,7 @@ module.exports = class QueryLexer {
|
|
|
49
60
|
const isSubQuery = tree.node.model !== 'root';
|
|
50
61
|
debug({ isSubQuery, startAt });
|
|
51
62
|
|
|
52
|
-
// Token is accumulated char-by-char.
|
|
63
|
+
// Token is String, accumulated char-by-char.
|
|
53
64
|
let token = '';
|
|
54
65
|
// Value of param ('id=10' OR 'fields=id,text').
|
|
55
66
|
let value = [];
|
|
@@ -71,22 +82,29 @@ module.exports = class QueryLexer {
|
|
|
71
82
|
token = '';
|
|
72
83
|
continue;
|
|
73
84
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
|
|
86
|
+
// If FN token:
|
|
87
|
+
if (FN_TOKENS.asArray.indexOf(token) > -1) {
|
|
88
|
+
// Set function token.
|
|
89
|
+
tree.node.fn = token;
|
|
78
90
|
token = '';
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
79
93
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
// If model subquery:
|
|
95
|
+
const model = token;
|
|
96
|
+
tree.use(model) ?? tree.include(model).use(model);
|
|
97
|
+
token = '';
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
// Process subquery:
|
|
100
|
+
i++;
|
|
101
|
+
const [ charsCount ] = this.parseIsolatedQuery(queryString, i, tree);
|
|
102
|
+
i += charsCount;
|
|
87
103
|
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
previousActive = model;
|
|
105
|
+
tree.up();
|
|
106
|
+
|
|
107
|
+
continue;
|
|
90
108
|
}
|
|
91
109
|
|
|
92
110
|
// ) can mean end of OP token params,
|
|
@@ -126,6 +144,40 @@ module.exports = class QueryLexer {
|
|
|
126
144
|
continue;
|
|
127
145
|
}
|
|
128
146
|
|
|
147
|
+
// If end of FN token:
|
|
148
|
+
if (!!tree.node.fn) {
|
|
149
|
+
// If token is empty, error:
|
|
150
|
+
if (token === '') {
|
|
151
|
+
const err = UnexpectedCharError(i, char);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let fnParams = {};
|
|
156
|
+
switch (tree.node.fn) {
|
|
157
|
+
case 'count':
|
|
158
|
+
fnParams = {
|
|
159
|
+
fn: 'count',
|
|
160
|
+
args: [token]
|
|
161
|
+
};
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
fnParams = {
|
|
165
|
+
fn: [tree.node.fn],
|
|
166
|
+
args: [token]
|
|
167
|
+
};
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
tree.node.addFunction(fnParams);
|
|
172
|
+
|
|
173
|
+
// Reset:
|
|
174
|
+
tree.node.resetFN();
|
|
175
|
+
tree.node.activeParam = 'includes';
|
|
176
|
+
token = '';
|
|
177
|
+
value = [];
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
129
181
|
// If end of subquery:
|
|
130
182
|
if (!!tree.node.activeParam && tree.node.activeParam !== 'includes') {
|
|
131
183
|
// Set value.
|
|
@@ -323,7 +375,7 @@ module.exports = class QueryLexer {
|
|
|
323
375
|
const param = this.parseParamFromToken(token);
|
|
324
376
|
|
|
325
377
|
if (isSubQuery === true && param === 'includes') {
|
|
326
|
-
const err = new TypeError(`'include' is forbidden inside subquery (position ${ i }). Use: 'model.
|
|
378
|
+
const err = new TypeError(`'include' is forbidden inside subquery (position ${ i }). Use: 'model.model1' or 'model.model1+model2'.`);
|
|
327
379
|
throw err;
|
|
328
380
|
}
|
|
329
381
|
|
package/lib/models/define.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
1
7
|
// CRUD mixins.
|
|
2
8
|
const { implementsCRUD } = require('./mixins');
|
|
3
9
|
// ORM.
|
|
4
10
|
const { DataTypes } = require('sequelize');
|
|
5
|
-
// NQL.
|
|
6
|
-
const Colander = require('../queries/Colander');
|
|
7
11
|
|
|
8
12
|
|
|
9
13
|
module.exports = defineModel;
|
|
@@ -52,7 +56,7 @@ function defineModel(
|
|
|
52
56
|
implementsCRUD(model);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
//
|
|
59
|
+
// Associations:
|
|
56
60
|
model.associate = (models) => {};
|
|
57
61
|
model.getIncludesList = _getIncludesList.bind(model);
|
|
58
62
|
|
|
@@ -61,7 +65,6 @@ function defineModel(
|
|
|
61
65
|
const values = { ...this.get() };
|
|
62
66
|
return values;
|
|
63
67
|
}
|
|
64
|
-
// Instance methods\
|
|
65
68
|
|
|
66
69
|
return model;
|
|
67
70
|
}
|
package/lib/models/mixins.js
CHANGED
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
|
|
11
11
|
// Nodester query:
|
|
12
12
|
const NQLexer = require('../middlewares/ql/sequelize/interpreter/QueryLexer');
|
|
13
|
-
const traverseNQuery = require('
|
|
13
|
+
const traverseNQuery = require('nodester/query/traverse');
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
module.exports = {
|