nodester 0.2.0 → 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.
- package/Readme.md +9 -6
- package/lib/application/index.js +11 -0
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +8 -1
- package/lib/query/traverse.js +4 -3
- package/lib/structures/Filter.js +2 -1
- package/package.json +1 -1
- package/tests/index.test.js +4 -4
- package/tests/nql.test.js +18 -1
package/Readme.md
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# nodester
|
|
2
|
-
> A robust and flexible boilerplate framework that makes iterative development easy.
|
|
3
2
|
|
|
4
3
|
[](https://www.npmjs.com/package/nodester)
|
|
5
4
|
[](https://www.npmjs.com/package/nodester)
|
|
6
5
|
|
|
6
|
+
> **nodester** is a modern and versatile Node.js framework designed to streamline the development of robust and scalable web applications.
|
|
7
|
+
|
|
8
|
+
**nodester Query Language (NQL):** The main reason of nodester's existence is the [nodester Query Language (NQL)](docs/Queries.md), an extension of standard REST API syntax, it lets you craft complex queries with hierarchical associations.
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
## Installation
|
|
9
12
|
|
|
@@ -18,7 +21,6 @@ npm install -S nodester
|
|
|
18
21
|
|
|
19
22
|
- [Usage](#usage)
|
|
20
23
|
- [Documentation](#documentation)
|
|
21
|
-
- [Extending App](#extending-application-functionality)
|
|
22
24
|
- [Philosophy](#philosophy)
|
|
23
25
|
- [License](#license)
|
|
24
26
|
- [Copyright](#copyright)
|
|
@@ -47,10 +49,11 @@ app.listen(8080, function() {
|
|
|
47
49
|
[Core concepts documentation ➡️](docs/CoreConcepts.md)
|
|
48
50
|
|
|
49
51
|
|
|
50
|
-
### Queries & Querying - Nodester Query Language (
|
|
51
|
-
|
|
52
|
+
### Queries & Querying - Nodester Query Language (NQL)
|
|
53
|
+
The true strength of nodester lies in its query language. Serving as an extension of standard REST API syntax, it brings many aspects of SQL into REST requests, providing developers with a simple yet potent tool for expressive and efficient data querying.
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
Read more about it in the documentation:
|
|
56
|
+
[NQL documentaion ➡️](docs/Queries.md)
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
### Database
|
|
@@ -70,7 +73,7 @@ The Philosophy of `nodester` is to provide a developer with a tool that can buil
|
|
|
70
73
|
|
|
71
74
|
### Goal
|
|
72
75
|
|
|
73
|
-
The goal of `nodester` is to be a robust and flexible framework that makes development in iteratations easy,
|
|
76
|
+
The goal of `nodester` is to be a robust and flexible framework that makes development in iteratations easy, while laying the foundation for seamless scalability in the future.
|
|
74
77
|
|
|
75
78
|
|
|
76
79
|
## LICENSE
|
package/lib/application/index.js
CHANGED
|
@@ -134,6 +134,17 @@ module.exports = class NodesterApplication extends Emitter {
|
|
|
134
134
|
return this._router.isLocked;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Indicates whether app is awaiting requests.
|
|
139
|
+
*
|
|
140
|
+
* @return {Boolean} isListening
|
|
141
|
+
*
|
|
142
|
+
* @api public
|
|
143
|
+
*/
|
|
144
|
+
get isListening() {
|
|
145
|
+
return this._isListening;
|
|
146
|
+
}
|
|
147
|
+
|
|
137
148
|
// Getters\
|
|
138
149
|
|
|
139
150
|
/*
|
|
@@ -222,6 +222,13 @@ module.exports = class QueryLexer {
|
|
|
222
222
|
const model = token;
|
|
223
223
|
tree.use(model) ?? tree.include(model);
|
|
224
224
|
|
|
225
|
+
// Last token (model) was included,
|
|
226
|
+
// now jump to root and proceed to collect next token (model).
|
|
227
|
+
tree.node.resetActiveParam();
|
|
228
|
+
tree.upToRoot();
|
|
229
|
+
|
|
230
|
+
tree.node.activeParam = 'includes';
|
|
231
|
+
|
|
225
232
|
token = '';
|
|
226
233
|
continue;
|
|
227
234
|
}
|
|
@@ -403,7 +410,7 @@ module.exports = class QueryLexer {
|
|
|
403
410
|
const err = MissingCharError(i+1, ')');
|
|
404
411
|
throw err;
|
|
405
412
|
}
|
|
406
|
-
|
|
413
|
+
|
|
407
414
|
this.setNodeParam(tree.node, token, value);
|
|
408
415
|
|
|
409
416
|
// If end of subquery:
|
package/lib/query/traverse.js
CHANGED
|
@@ -261,11 +261,12 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
261
261
|
|
|
262
262
|
|
|
263
263
|
// Includes:
|
|
264
|
-
//
|
|
264
|
+
// Validate, if requested includes are available:
|
|
265
265
|
for (let include of includes) {
|
|
266
266
|
const includeName = include.model;
|
|
267
|
+
|
|
267
268
|
if (rootModelAssociations[includeName] === undefined) {
|
|
268
|
-
const err = new NQueryError(`No include named '${ includeName }'
|
|
269
|
+
const err = new NQueryError(`No include named '${ includeName }'`);
|
|
269
270
|
err.status = httpCodes.NOT_ACCEPTABLE;
|
|
270
271
|
Error.captureStackTrace(err, traverse);
|
|
271
272
|
throw err;
|
|
@@ -300,7 +301,7 @@ function _traverseIncludes(includes, model, filter, resultQuery) {
|
|
|
300
301
|
|
|
301
302
|
// If no such association:
|
|
302
303
|
if (!association) {
|
|
303
|
-
const err = new NQueryError(`No include named '${ includeName }'
|
|
304
|
+
const err = new NQueryError(`No include named '${ includeName }'`);
|
|
304
305
|
err.status = httpCodes.NOT_ACCEPTABLE;
|
|
305
306
|
Error.captureStackTrace(err, _traverseIncludes);
|
|
306
307
|
throw err;
|
package/lib/structures/Filter.js
CHANGED
|
@@ -97,7 +97,8 @@ module.exports = class NodesterFilter {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
// If singular association:
|
|
100
|
-
|
|
100
|
+
// Fix of "Only HasMany associations support include.separate"
|
|
101
|
+
if ('HasMany' !== association.associationType) {
|
|
101
102
|
// Empty bounds.
|
|
102
103
|
includeFilter.noBounds = true;
|
|
103
104
|
}
|
package/package.json
CHANGED
package/tests/index.test.js
CHANGED
|
@@ -27,13 +27,13 @@ describe('nodester application', () => {
|
|
|
27
27
|
test('Application start', () => {
|
|
28
28
|
app.listen(PORT, function() {
|
|
29
29
|
expect(app.port).toBe(PORT);
|
|
30
|
-
expect(app.
|
|
31
|
-
expect(app.
|
|
30
|
+
expect(app.isLocked).toBe(true);
|
|
31
|
+
expect(app.isListening).toBe(true);
|
|
32
|
+
expect(app.middlewaresStack.length).toBe(4);
|
|
32
33
|
|
|
33
34
|
app.stop();
|
|
34
35
|
|
|
35
|
-
expect(app.
|
|
36
|
-
expect(app.router._middlewares.isLocked).toBe(false);
|
|
36
|
+
expect(app.isLocked).toBe(false);
|
|
37
37
|
expect(app.isListening).toBe(false);
|
|
38
38
|
});
|
|
39
39
|
});
|
package/tests/nql.test.js
CHANGED
|
@@ -49,6 +49,9 @@ describe('nodester Query Language', () => {
|
|
|
49
49
|
|
|
50
50
|
// Like simple.
|
|
51
51
|
'title=like(some_text)',
|
|
52
|
+
|
|
53
|
+
// Subinclude and isolated Horizontal.
|
|
54
|
+
'in=comments.user,likes',
|
|
52
55
|
];
|
|
53
56
|
|
|
54
57
|
it('query "Simple where"', () => {
|
|
@@ -262,7 +265,6 @@ describe('nodester Query Language', () => {
|
|
|
262
265
|
|
|
263
266
|
expect(result).toMatchObject(expected);
|
|
264
267
|
});
|
|
265
|
-
|
|
266
268
|
|
|
267
269
|
test('Token "Like" simple', () => {
|
|
268
270
|
const lexer = new QueryLexer( queryStrings[14] );
|
|
@@ -274,4 +276,19 @@ describe('nodester Query Language', () => {
|
|
|
274
276
|
|
|
275
277
|
expect(result).toMatchObject(expected);
|
|
276
278
|
});
|
|
279
|
+
|
|
280
|
+
it('query "Subinclude and isolated Horizontal"', () => {
|
|
281
|
+
const lexer = new QueryLexer( queryStrings[15] );
|
|
282
|
+
result = lexer.query;
|
|
283
|
+
|
|
284
|
+
const tree = new ModelsTree();
|
|
285
|
+
tree.include('comments').use('comments');
|
|
286
|
+
tree.include('user');
|
|
287
|
+
tree.up();
|
|
288
|
+
tree.include('likes');
|
|
289
|
+
const expected = tree.root.toObject();
|
|
290
|
+
|
|
291
|
+
expect(result).toMatchObject(expected);
|
|
292
|
+
});
|
|
293
|
+
|
|
277
294
|
});
|