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.
- package/Readme.md +33 -39
- package/lib/application/index.js +110 -38
- package/lib/constants/Operations.js +23 -0
- package/lib/controllers/methods/index.js +194 -0
- package/lib/controllers/mixins/index.js +222 -0
- package/lib/database/connection.js +34 -0
- package/lib/database/migration.js +42 -0
- package/lib/database/utils.js +19 -0
- package/lib/enums/Enum.js +16 -0
- package/lib/facades/methods/index.js +173 -0
- package/lib/facades/mixins/index.js +111 -0
- package/lib/factories/errors/CustomError.js +7 -5
- package/lib/factories/responses/html.js +7 -2
- package/lib/factories/responses/rest.js +110 -0
- package/lib/http/codes/index.js +157 -0
- package/lib/{application/http → http}/request.js +6 -30
- package/lib/{application/http → http}/response.js +20 -53
- package/lib/middlewares/etag/index.js +62 -0
- package/lib/middlewares/ql/sequelize/index.js +34 -0
- package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +121 -0
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +456 -0
- package/lib/models/associate.js +17 -0
- package/lib/models/define.js +56 -14
- package/lib/models/mixins.js +100 -78
- package/lib/params/Params.js +37 -0
- package/lib/queries/Colander.js +84 -0
- package/lib/queries/NodesterQueryParams.js +139 -0
- package/lib/queries/traverse.js +311 -0
- package/lib/router/handlers.util.js +61 -0
- package/lib/router/index.js +386 -0
- package/lib/router/route.js +124 -0
- package/lib/router/routes.util.js +66 -0
- package/lib/stacks/MarkersStack.js +35 -0
- package/lib/{application → stacks}/MiddlewareStack.js +47 -13
- package/lib/utils/path.util.js +3 -1
- package/lib/utils/types.util.js +51 -1
- package/lib/validators/dates.js +25 -0
- package/lib/validators/numbers.js +14 -0
- package/package.json +31 -4
- package/tests/index.test.js +7 -2
- package/tests/nql.test.js +277 -0
- package/docs/App.md +0 -13
- package/docs/Queries.md +0 -61
- package/docs/Readme.md +0 -2
- package/docs/Routing.md +0 -34
- package/examples/goal/index.js +0 -23
- package/examples/rest/index.js +0 -25
- package/examples/rest/node_modules/.package-lock.json +0 -40
- package/examples/rest/package-lock.json +0 -72
- package/examples/rest/package.json +0 -14
- package/lib/constants/ConstantsEnum.js +0 -13
- package/lib/controllers/Controller.js +0 -474
- package/lib/controllers/JWTController.js +0 -240
- package/lib/controllers/ServiceController.js +0 -109
- package/lib/controllers/WebController.js +0 -75
- package/lib/facades/Facade.js +0 -388
- package/lib/facades/FacadeParams.js +0 -11
- package/lib/facades/ServiceFacade.js +0 -17
- package/lib/facades/jwt.facade.js +0 -273
- package/lib/factories/responses/api.js +0 -90
- package/lib/models/DisabledRefreshToken.js +0 -68
- package/lib/models/Extractor.js +0 -320
- package/lib/routers/Default/index.js +0 -143
- package/lib/routers/Default/layer.js +0 -50
- package/lib/routers/Main/index.js +0 -10
- package/lib/routers/Roles/index.js +0 -81
- package/lib/utils/params.util.js +0 -19
- /package/lib/{application/http → http}/utils.js +0 -0
|
@@ -4,12 +4,13 @@ const debug = require('debug')('nodester:MiddlewareStack');
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
module.exports = class MiddlewareStack {
|
|
7
|
-
constructor() {
|
|
7
|
+
constructor(opts={}) {
|
|
8
8
|
// This array MUST stay flat!
|
|
9
9
|
this.middlewares = [];
|
|
10
10
|
|
|
11
11
|
// Indicates whether we can add more middlewares or no.
|
|
12
12
|
this.isLocked = false;
|
|
13
|
+
this.finalhandlerEnabled = !!opts.finalhandlerEnabled;
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
const env = process.env.NODE_ENV || 'development';
|
|
@@ -25,11 +26,12 @@ module.exports = class MiddlewareStack {
|
|
|
25
26
|
* Add the given middleware `fn` to the stack.
|
|
26
27
|
*
|
|
27
28
|
* @param {Function} fn
|
|
29
|
+
* @param {Integer} index (0 or undefined)
|
|
28
30
|
* @return {Integer} index of new middleware
|
|
29
31
|
*
|
|
30
32
|
* @api public
|
|
31
33
|
*/
|
|
32
|
-
add(fn) {
|
|
34
|
+
add(fn, index) {
|
|
33
35
|
if (this.isLocked) {
|
|
34
36
|
const err = new Error(`Can't add more middlewares while stack is locked.`);
|
|
35
37
|
throw err;
|
|
@@ -40,9 +42,23 @@ module.exports = class MiddlewareStack {
|
|
|
40
42
|
throw err;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
if (!!index && isNaN(index)) {
|
|
46
|
+
const err = new TypeError('"index" must be an Integer!');
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let pushedIndex = -1;
|
|
51
|
+
|
|
52
|
+
if (index === 0) {
|
|
53
|
+
this.middlewares.unshift(fn);
|
|
54
|
+
pushedIndex = 0;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
pushedIndex = this.middlewares.push(fn);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
debug(`added middleware (${ pushedIndex })`);
|
|
61
|
+
return pushedIndex;
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
|
|
@@ -65,7 +81,7 @@ module.exports = class MiddlewareStack {
|
|
|
65
81
|
throw err;
|
|
66
82
|
}
|
|
67
83
|
|
|
68
|
-
this.middlewares.splice(
|
|
84
|
+
this.middlewares.splice(index, 1);
|
|
69
85
|
debug(`removed middleware (${ index })`);
|
|
70
86
|
return this;
|
|
71
87
|
}
|
|
@@ -77,8 +93,10 @@ module.exports = class MiddlewareStack {
|
|
|
77
93
|
* @api public
|
|
78
94
|
*/
|
|
79
95
|
lock() {
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
if (this.finalhandlerEnabled) {
|
|
97
|
+
// Add final handler to the stack.
|
|
98
|
+
this.add((req, res)=>this.finalhandler(req, res)());
|
|
99
|
+
}
|
|
82
100
|
|
|
83
101
|
// Stack is ready.
|
|
84
102
|
this.isLocked = true;
|
|
@@ -94,7 +112,10 @@ module.exports = class MiddlewareStack {
|
|
|
94
112
|
*/
|
|
95
113
|
unlock() {
|
|
96
114
|
this.isLocked = false;
|
|
97
|
-
|
|
115
|
+
|
|
116
|
+
if (this.finalhandlerEnabled) {
|
|
117
|
+
this.middlewares.pop();
|
|
118
|
+
}
|
|
98
119
|
|
|
99
120
|
debug(`stack is unlocked`);
|
|
100
121
|
}
|
|
@@ -105,21 +126,34 @@ module.exports = class MiddlewareStack {
|
|
|
105
126
|
*
|
|
106
127
|
* @api public
|
|
107
128
|
*/
|
|
108
|
-
process(req, res) {
|
|
129
|
+
process(req, res, next) {
|
|
109
130
|
let middlewareOffset = -1;
|
|
110
131
|
|
|
111
|
-
const
|
|
132
|
+
const _next = (...args) => {
|
|
112
133
|
middlewareOffset += 1;
|
|
113
134
|
const fn = this.middlewares[middlewareOffset];
|
|
114
135
|
|
|
115
136
|
try {
|
|
116
|
-
|
|
137
|
+
if (!fn && !next) {
|
|
138
|
+
const err = new TypeError(`Handler for ${ req.method } ${ req.url } is not defined.`);
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
else if (!fn && !!next) {
|
|
142
|
+
return next.call(null, req, res, next, ...args);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return fn.call(null, req, res, _next, ...args);
|
|
117
146
|
}
|
|
118
147
|
catch(error) {
|
|
119
148
|
return this.finalhandler(req, res)(error);
|
|
120
149
|
}
|
|
121
150
|
}
|
|
122
151
|
|
|
123
|
-
return
|
|
152
|
+
return _next();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
get length() {
|
|
157
|
+
return this.middlewares.length;
|
|
124
158
|
}
|
|
125
159
|
}
|
package/lib/utils/path.util.js
CHANGED
package/lib/utils/types.util.js
CHANGED
|
@@ -2,7 +2,10 @@ const objectRegExp = /^\[object (\S+)\]$/;
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
|
-
typeOf: _typeOf
|
|
5
|
+
typeOf: _typeOf,
|
|
6
|
+
isAsync: _isAsync,
|
|
7
|
+
isRegExp: _isRegExp,
|
|
8
|
+
isConstructor: _isConstructor
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
|
|
@@ -24,3 +27,50 @@ function _typeOf(obj) {
|
|
|
24
27
|
.call(obj)
|
|
25
28
|
.replace(objectRegExp, '$1');
|
|
26
29
|
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @alias isAsync
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
function _isAsync(fn) {
|
|
38
|
+
if (!fn) {
|
|
39
|
+
const err = new TypeError('"fn" is not defined.');
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return fn.constructor.name === 'AsyncFunction';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
*
|
|
49
|
+
* @alias isRegExp
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
function _isRegExp(obj) {
|
|
53
|
+
if (!obj) {
|
|
54
|
+
const err = new TypeError('"obj" is not defined.');
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return obj instanceof RegExp;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @alias isConstructor
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
function _isConstructor(fn) {
|
|
68
|
+
try {
|
|
69
|
+
new fn();
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
isValidDate: _isValidDate
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {Any} dateToTest
|
|
9
|
+
*
|
|
10
|
+
* @alias isValidDate
|
|
11
|
+
* @api public
|
|
12
|
+
*/
|
|
13
|
+
function _isValidDate(dateToTest) {
|
|
14
|
+
// If number:
|
|
15
|
+
if (!isNaN(dateToTest)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (typeof dateToTest === 'string') {
|
|
20
|
+
const date = new Date(dateToTest);
|
|
21
|
+
return date.toString() !== 'Invalid Date';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return dateToTest instanceof Date;
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodester",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "A boilerplate framework for Node.js",
|
|
5
5
|
"exports": {
|
|
6
|
-
".": "./lib/application/index.js"
|
|
6
|
+
".": "./lib/application/index.js",
|
|
7
|
+
"./controllers/methods": "./lib/controllers/methods/index.js",
|
|
8
|
+
"./controllers/mixins": "./lib/controllers/mixins/index.js",
|
|
9
|
+
"./database/connection": "./lib/database/connection.js",
|
|
10
|
+
"./database/migration": "./lib/database/migration.js",
|
|
11
|
+
"./database/utils": "./lib/database/utils.js",
|
|
12
|
+
"./enum": "./lib/enums/Enum.js",
|
|
13
|
+
"./facades/methods": "./lib/facades/methods/index.js",
|
|
14
|
+
"./facades/mixins": "./lib/facades/mixins/index.js",
|
|
15
|
+
"./factories/errors": "./lib/factories/errors/index.js",
|
|
16
|
+
"./factories/responses/rest": "./lib/factories/responses/rest/index.js",
|
|
17
|
+
"./http/codes": "./lib/http/codes/index.js",
|
|
18
|
+
"./models/associate": "./lib/models/associate.js",
|
|
19
|
+
"./models/define": "./lib/models/define.js",
|
|
20
|
+
"./params": "./lib/params/Params.js",
|
|
21
|
+
"./ql/sequelize": "./lib/middlewares/ql/sequelize",
|
|
22
|
+
"./queries/Colander": "./lib/queries/Colander.js",
|
|
23
|
+
"./queries/traverse": "./lib/queries/traverse.js",
|
|
24
|
+
"./route": "./lib/router/route.js",
|
|
25
|
+
"./router": "./lib/router/index.js",
|
|
26
|
+
"./utils/strings": "./lib/utils/strings.util.js"
|
|
7
27
|
},
|
|
8
28
|
"directories": {
|
|
9
29
|
"doc": "docs"
|
|
@@ -12,8 +32,8 @@
|
|
|
12
32
|
"lib"
|
|
13
33
|
],
|
|
14
34
|
"scripts": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
35
|
+
"test": "jest",
|
|
36
|
+
"examples:rest": "node ./examples/rest/index.js"
|
|
17
37
|
},
|
|
18
38
|
"author": "Mark Khramko <markkhramko@gmail.com>",
|
|
19
39
|
"license": "MIT",
|
|
@@ -35,17 +55,24 @@
|
|
|
35
55
|
"api"
|
|
36
56
|
],
|
|
37
57
|
"dependencies": {
|
|
58
|
+
"@js-temporal/polyfill": "^0.4.3",
|
|
38
59
|
"accepts": "^1.3.8",
|
|
60
|
+
"body-parser": "^1.20.2",
|
|
61
|
+
"common-js-file-extensions": "^1.0.4",
|
|
39
62
|
"content-disposition": "^0.5.4",
|
|
40
63
|
"content-type": "^1.0.5",
|
|
41
64
|
"cookie": "^0.5.0",
|
|
42
65
|
"cookie-signature": "^1.2.0",
|
|
43
66
|
"debug": "^4.3.4",
|
|
67
|
+
"etag": "^1.8.1",
|
|
44
68
|
"finalhandler": "^1.2.0",
|
|
45
69
|
"formidable": "^1.2.6",
|
|
46
70
|
"fresh": "^0.5.2",
|
|
47
71
|
"http-errors": "^2.0.0",
|
|
48
72
|
"inflection": "^2.0.1",
|
|
73
|
+
"mysql2": "^3.6.0",
|
|
74
|
+
"pg": "^8.11.3",
|
|
75
|
+
"pg-hstore": "^2.3.4",
|
|
49
76
|
"proxy-addr": "^2.0.7",
|
|
50
77
|
"qs": "^6.11.0",
|
|
51
78
|
"range-parser": "^1.2.1",
|
package/tests/index.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Test utils
|
|
1
|
+
// Test utils.
|
|
2
2
|
const {
|
|
3
3
|
describe,
|
|
4
4
|
it,
|
|
@@ -24,11 +24,16 @@ describe('nodester application', () => {
|
|
|
24
24
|
// app.setDatabase();
|
|
25
25
|
// app.set.database();
|
|
26
26
|
|
|
27
|
-
test('
|
|
27
|
+
test('Application start', () => {
|
|
28
28
|
app.listen(PORT, function() {
|
|
29
29
|
expect(app.port).toBe(PORT);
|
|
30
|
+
expect(app.router._middlewares.isLocked).toBe(true);
|
|
31
|
+
expect(app.router._middlewares.length).toBe(2);
|
|
30
32
|
|
|
31
33
|
app.stop();
|
|
34
|
+
|
|
35
|
+
expect(app.router._middlewares.length).toBe(0);
|
|
36
|
+
expect(app.router._middlewares.isLocked).toBe(false);
|
|
32
37
|
expect(app.isListening).toBe(false);
|
|
33
38
|
});
|
|
34
39
|
});
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// Test utils.
|
|
2
|
+
const {
|
|
3
|
+
describe,
|
|
4
|
+
it,
|
|
5
|
+
expect,
|
|
6
|
+
test
|
|
7
|
+
} = require('@jest/globals');
|
|
8
|
+
|
|
9
|
+
// Parser:
|
|
10
|
+
const { ModelsTree } = require('../lib/middlewares/ql/sequelize/interpreter/ModelsTree');
|
|
11
|
+
const QueryLexer = require('../lib/middlewares/ql/sequelize/interpreter/QueryLexer');
|
|
12
|
+
|
|
13
|
+
describe('nodester Query Language', () => {
|
|
14
|
+
const queryStrings = [
|
|
15
|
+
// Simple where.
|
|
16
|
+
'id=10',
|
|
17
|
+
// All possible params.
|
|
18
|
+
'id=10&position=4&limit=3&skip=10&order=desc&order_by=index&fields=id,content,position,created_at',
|
|
19
|
+
// Simple includes.
|
|
20
|
+
'includes=comments&id=7',
|
|
21
|
+
// Include with All possible params.
|
|
22
|
+
'includes=comments(id=10&position=4&limit=3&skip=10&order=desc&order_by=index&fields=id,content,position)',
|
|
23
|
+
|
|
24
|
+
// Subinclude horizontal.
|
|
25
|
+
'includes=comments,users&id=1000',
|
|
26
|
+
// Subinclude horizontal (more entries).
|
|
27
|
+
'includes=comments(order=desc),users,likes(order=rand),reposts&id=1000',
|
|
28
|
+
// Subinclude horizontal (+ syntaxis).
|
|
29
|
+
'includes=comments(order=desc).users+likes(order=rand&order_by=position)&id=1000',
|
|
30
|
+
|
|
31
|
+
// Subinclude vertical.
|
|
32
|
+
'includes=comments.users&id=1000',
|
|
33
|
+
// Subinclude vertical (more entries).
|
|
34
|
+
'in=comments.users.avatars.sizes&position=200',
|
|
35
|
+
|
|
36
|
+
// Complex includes.
|
|
37
|
+
'includes=comments.users.avatars(fields=id,content&order=rand)&id=7&limit=3',
|
|
38
|
+
|
|
39
|
+
// Broken includes.
|
|
40
|
+
'includes=comments(order=rand)&id=7&limit=3&includes=users(fields=id,content)',
|
|
41
|
+
|
|
42
|
+
// OR simple.
|
|
43
|
+
'or(index=2,position=5)',
|
|
44
|
+
// OR shortened.
|
|
45
|
+
'|(index=2,position=5)',
|
|
46
|
+
|
|
47
|
+
// NOT inside include.
|
|
48
|
+
'includes=comments(id=not(7))',
|
|
49
|
+
|
|
50
|
+
// Like simple.
|
|
51
|
+
'title=like(some_text)',
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
it('query "Simple where"', () => {
|
|
55
|
+
const lexer = new QueryLexer( queryStrings[0] );
|
|
56
|
+
const result = lexer.query;
|
|
57
|
+
|
|
58
|
+
const tree = new ModelsTree();
|
|
59
|
+
tree.node.addWhere({ id: ['10'] });
|
|
60
|
+
const expected = tree.root.toObject();
|
|
61
|
+
|
|
62
|
+
expect(result).toMatchObject(expected);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('query "All possible params"', () => {
|
|
66
|
+
const lexer = new QueryLexer( queryStrings[1] );
|
|
67
|
+
const result = lexer.query;
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
const tree = new ModelsTree();
|
|
71
|
+
tree.node.addWhere({ id: ['10'], position: ['4'] });
|
|
72
|
+
tree.node.fields = [ 'id', 'content', 'position', 'created_at' ];
|
|
73
|
+
tree.node.limit = 3;
|
|
74
|
+
tree.node.skip = 10;
|
|
75
|
+
tree.node.order = 'desc';
|
|
76
|
+
tree.node.order_by = 'index';
|
|
77
|
+
const expected = tree.root.toObject();
|
|
78
|
+
|
|
79
|
+
expect(result).toMatchObject(expected);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('query "Simple includes"', () => {
|
|
83
|
+
const lexer = new QueryLexer( queryStrings[2] );
|
|
84
|
+
const result = lexer.query;
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
const tree = new ModelsTree();
|
|
88
|
+
tree.node.addWhere({ id: ['7'] });
|
|
89
|
+
tree.include('comments');
|
|
90
|
+
const expected = tree.root.toObject();
|
|
91
|
+
|
|
92
|
+
expect(result).toMatchObject(expected);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('query "Include with all possible params"', () => {
|
|
96
|
+
const lexer = new QueryLexer( queryStrings[3] );
|
|
97
|
+
const result = lexer.query;
|
|
98
|
+
|
|
99
|
+
const tree = new ModelsTree();
|
|
100
|
+
tree.include('comments').use('comments');
|
|
101
|
+
tree.node.addWhere({ id: ['10'], position: ['4'] });
|
|
102
|
+
tree.node.fields = [ 'id', 'content', 'position' ];
|
|
103
|
+
tree.node.limit = 3;
|
|
104
|
+
tree.node.skip = 10;
|
|
105
|
+
tree.node.order = 'desc';
|
|
106
|
+
tree.node.order_by = 'index';
|
|
107
|
+
const expected = tree.root.toObject();
|
|
108
|
+
|
|
109
|
+
expect(result).toMatchObject(expected);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
test('query "Subinclude horizontal"', () => {
|
|
114
|
+
const lexer = new QueryLexer( queryStrings[4] );
|
|
115
|
+
const result = lexer.query;
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
const tree = new ModelsTree();
|
|
119
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
120
|
+
tree.include('comments');
|
|
121
|
+
tree.include('users');
|
|
122
|
+
const expected = tree.root.toObject();
|
|
123
|
+
|
|
124
|
+
expect(result).toMatchObject(expected);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('query "Subinclude horizontal (complex)"', () => {
|
|
128
|
+
const lexer = new QueryLexer( queryStrings[5] );
|
|
129
|
+
const result = lexer.query;
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
const tree = new ModelsTree();
|
|
133
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
134
|
+
tree.include('comments').use('comments');
|
|
135
|
+
tree.node.order = 'desc';
|
|
136
|
+
tree.up();
|
|
137
|
+
tree.include('users');
|
|
138
|
+
tree.include('likes') && tree.use('likes');
|
|
139
|
+
tree.node.order = 'rand';
|
|
140
|
+
tree.up();
|
|
141
|
+
tree.include('reposts');
|
|
142
|
+
const expected = tree.root.toObject();
|
|
143
|
+
|
|
144
|
+
expect(result).toMatchObject(expected);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
test('query "Subinclude horizontal (+ syntaxis)"', () => {
|
|
149
|
+
const lexer = new QueryLexer( queryStrings[6] );
|
|
150
|
+
const result = lexer.query;
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
const tree = new ModelsTree();
|
|
154
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
155
|
+
tree.include('comments').use('comments');
|
|
156
|
+
tree.node.order = 'desc';
|
|
157
|
+
tree.include('users');
|
|
158
|
+
tree.include('likes') && tree.use('likes');
|
|
159
|
+
tree.node.order = 'rand';
|
|
160
|
+
tree.node.order_by = 'position';
|
|
161
|
+
tree.up();
|
|
162
|
+
const expected = tree.root.toObject();
|
|
163
|
+
|
|
164
|
+
expect(result).toMatchObject(expected);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
test('query "Subinclude vertical"', () => {
|
|
169
|
+
const lexer = new QueryLexer( queryStrings[7] );
|
|
170
|
+
const result = lexer.query;
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
const tree = new ModelsTree();
|
|
174
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
175
|
+
tree.include('comments').use('comments');
|
|
176
|
+
tree.include('users');
|
|
177
|
+
const expected = tree.root.toObject();
|
|
178
|
+
|
|
179
|
+
expect(result).toMatchObject(expected);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('query "Subinclude vertical (complex)"', () => {
|
|
183
|
+
const lexer = new QueryLexer( queryStrings[8] );
|
|
184
|
+
const result = lexer.query;
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
const tree = new ModelsTree();
|
|
188
|
+
tree.node.addWhere({ position: ['200'] });
|
|
189
|
+
tree.include('comments').use('comments');
|
|
190
|
+
tree.include('users').use('users');
|
|
191
|
+
tree.include('avatars').use('avatars');
|
|
192
|
+
tree.include('sizes').use('sizes');
|
|
193
|
+
const expected = tree.root.toObject();
|
|
194
|
+
|
|
195
|
+
expect(result).toMatchObject(expected);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('query "Complex includes"', () => {
|
|
199
|
+
const lexer = new QueryLexer( queryStrings[9] );
|
|
200
|
+
const result = lexer.query;
|
|
201
|
+
|
|
202
|
+
const tree = new ModelsTree();
|
|
203
|
+
tree.node.addWhere({ id: ['7'] });
|
|
204
|
+
tree.node.limit = 3;
|
|
205
|
+
tree.include('comments').use('comments');
|
|
206
|
+
tree.include('users').use('users');
|
|
207
|
+
tree.include('avatars').use('avatars');
|
|
208
|
+
tree.node.fields = [ 'id', 'content' ];
|
|
209
|
+
tree.node.order = 'rand';
|
|
210
|
+
const expected = tree.root.toObject();
|
|
211
|
+
|
|
212
|
+
expect(result).toMatchObject(expected);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('query "Broken includes"', () => {
|
|
216
|
+
const lexer = new QueryLexer( queryStrings[10] );
|
|
217
|
+
const result = lexer.query;
|
|
218
|
+
|
|
219
|
+
const tree = new ModelsTree();
|
|
220
|
+
tree.node.addWhere({ id: ['7'] });
|
|
221
|
+
tree.node.limit = 3;
|
|
222
|
+
tree.include('comments').use('comments');
|
|
223
|
+
tree.node.order = 'rand';
|
|
224
|
+
tree.up();
|
|
225
|
+
tree.include('users').use('users');
|
|
226
|
+
tree.node.fields = [ 'id', 'content' ];
|
|
227
|
+
const expected = tree.root.toObject();
|
|
228
|
+
|
|
229
|
+
expect(result).toMatchObject(expected);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('Token "OR" simple', () => {
|
|
233
|
+
const lexer = new QueryLexer( queryStrings[11] );
|
|
234
|
+
const result = lexer.query;
|
|
235
|
+
|
|
236
|
+
const tree = new ModelsTree();
|
|
237
|
+
tree.node.addWhere({ or: [ { index: ['2'] }, { position: ['5'] } ] });
|
|
238
|
+
const expected = tree.root.toObject();
|
|
239
|
+
|
|
240
|
+
expect(result).toMatchObject(expected);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('Token "OR" shortened', () => {
|
|
244
|
+
const lexer = new QueryLexer( queryStrings[12] );
|
|
245
|
+
const result = lexer.query;
|
|
246
|
+
|
|
247
|
+
const tree = new ModelsTree();
|
|
248
|
+
tree.node.addWhere({ or: [ { index: ['2'] }, { position: ['5'] } ] });
|
|
249
|
+
const expected = tree.root.toObject();
|
|
250
|
+
|
|
251
|
+
expect(result).toMatchObject(expected);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('Token "NOT"', () => {
|
|
255
|
+
const lexer = new QueryLexer( queryStrings[13] );
|
|
256
|
+
const result = lexer.query;
|
|
257
|
+
|
|
258
|
+
const tree = new ModelsTree();
|
|
259
|
+
tree.include('comments').use('comments');
|
|
260
|
+
tree.node.addWhere({ id: { not: ['7'] }});
|
|
261
|
+
const expected = tree.root.toObject();
|
|
262
|
+
|
|
263
|
+
expect(result).toMatchObject(expected);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
test('Token "Like" simple', () => {
|
|
268
|
+
const lexer = new QueryLexer( queryStrings[14] );
|
|
269
|
+
const result = lexer.query;
|
|
270
|
+
|
|
271
|
+
const tree = new ModelsTree();
|
|
272
|
+
tree.node.addWhere({ title: { like: ['some_text'] }});
|
|
273
|
+
const expected = tree.root.toObject();
|
|
274
|
+
|
|
275
|
+
expect(result).toMatchObject(expected);
|
|
276
|
+
});
|
|
277
|
+
});
|
package/docs/App.md
DELETED
package/docs/Queries.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# nodester Queries API
|
|
2
|
-
|
|
3
|
-
## Like value
|
|
4
|
-
|
|
5
|
-
To emulate MySQL's `like %value% ` query in URL,
|
|
6
|
-
pass `?key=like(value)` in the query.
|
|
7
|
-
|
|
8
|
-
* Example:
|
|
9
|
-
`http://localhost:5001/api/v1/countries?name=like(Engl)`
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## NotLike value
|
|
13
|
-
|
|
14
|
-
* Example:
|
|
15
|
-
`http://localhost:5001/api/v1/countries?name=notLike(Engl)`
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## Or
|
|
19
|
-
|
|
20
|
-
To emulate MySQL's `where key=value or key=value` query in URL,
|
|
21
|
-
pass `?key=or(value1,value2)` in the query.
|
|
22
|
-
* ! Note: don't use `spaces` between values.
|
|
23
|
-
|
|
24
|
-
* Example:
|
|
25
|
-
`http://localhost:5001/api/v1/countries?name=or(England,Germany)`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Count
|
|
29
|
-
|
|
30
|
-
MySQL's `select count(value)` query is run by default in facade's `getMany` function
|
|
31
|
-
|
|
32
|
-
* Response Example:
|
|
33
|
-
```JSON
|
|
34
|
-
{
|
|
35
|
-
"count": 10,
|
|
36
|
-
"countries": [ ... ],
|
|
37
|
-
"limit": 10,
|
|
38
|
-
"skip": 0,
|
|
39
|
-
"total_count": 195
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Order (Sorting)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
#### Top level
|
|
47
|
-
|
|
48
|
-
`order_by` & `order` arguments can be set in `query`
|
|
49
|
-
`http://localhost:5001/api/v1/countries?order_by=id&order=desc`
|
|
50
|
-
|
|
51
|
-
Above `query` will sort Countries[] by it's id.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
#### Nested (Includes)
|
|
55
|
-
|
|
56
|
-
`http://localhost:5001/api/v1/countries?includes=cities(order_by=id&order=desc)`
|
|
57
|
-
|
|
58
|
-
Above `query` will sort Cities[] by it's id inside every Country object.
|
|
59
|
-
|
|
60
|
-
It can also do this:
|
|
61
|
-
`http://localhost:5001/api/v1/countries?includes=cities(order_by=id&order=desc).areas`
|
package/docs/Readme.md
DELETED