nodester 0.6.4 → 0.6.6
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/lib/constants/Operators.js +23 -0
- package/lib/middlewares/404/index.js +38 -0
- package/lib/middlewares/etag/index.js +81 -0
- package/lib/middlewares/render/index.js +62 -0
- package/lib/models/mixins.js +29 -12
- package/package.json +3 -3
- package/tests/ast.js +18 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const Enum = require('nodester/enum');
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module.exports = new Enum({
|
|
5
|
+
and: Symbol.for('and'),
|
|
6
|
+
between: Symbol.for('between'),
|
|
7
|
+
contains: Symbol.for('contains'),
|
|
8
|
+
eq: Symbol.for('eq'),
|
|
9
|
+
ne: Symbol.for('ne'),
|
|
10
|
+
gte: Symbol.for('gte'),
|
|
11
|
+
gt: Symbol.for('gt'),
|
|
12
|
+
lte: Symbol.for('lte'),
|
|
13
|
+
lt: Symbol.for('lt'),
|
|
14
|
+
not: Symbol.for('not'),
|
|
15
|
+
is: Symbol.for('is'),
|
|
16
|
+
in: Symbol.for('in'),
|
|
17
|
+
notIn: Symbol.for('notIn'),
|
|
18
|
+
like: Symbol.for('like'),
|
|
19
|
+
notLike: Symbol.for('notLike'),
|
|
20
|
+
notBetween: Symbol.for('notBetween'),
|
|
21
|
+
or: Symbol.for('or'),
|
|
22
|
+
xor: Symbol.for('xor'),
|
|
23
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
HTTP_CODE_NOT_FOUND
|
|
10
|
+
} = require('nodester/http/codes');
|
|
11
|
+
|
|
12
|
+
const { createErrorResponse } = require('nodester/factories/responses/rest');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize `404` middleware.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} [options]
|
|
19
|
+
*
|
|
20
|
+
* @return {Function}
|
|
21
|
+
*
|
|
22
|
+
* @access public
|
|
23
|
+
*/
|
|
24
|
+
module.exports = function init404Middleware(options={}) {
|
|
25
|
+
const context = {
|
|
26
|
+
options
|
|
27
|
+
}
|
|
28
|
+
return handle.bind(context);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function handle(req, res, next) {
|
|
32
|
+
const err = new Error('Route not found');
|
|
33
|
+
|
|
34
|
+
return createErrorResponse(res, {
|
|
35
|
+
error: err,
|
|
36
|
+
status: HTTP_CODE_NOT_FOUND
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const calculate = require('etag');
|
|
9
|
+
const Stream = require('stream');
|
|
10
|
+
|
|
11
|
+
// Utils:
|
|
12
|
+
const promisify = require('util').promisify;
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const getFileStats = promisify(fs.stat);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
module.exports = initETagMiddleware;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialize `etag` middleware.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} [options]
|
|
23
|
+
* @param {boolean} options.weak
|
|
24
|
+
*
|
|
25
|
+
* @return {Function}
|
|
26
|
+
*
|
|
27
|
+
* @access public
|
|
28
|
+
*/
|
|
29
|
+
function initETagMiddleware(options) {
|
|
30
|
+
const context = {
|
|
31
|
+
options
|
|
32
|
+
}
|
|
33
|
+
return handle.bind(context);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Add ETag header field.
|
|
38
|
+
*/
|
|
39
|
+
function handle(req, res, next) {
|
|
40
|
+
// console.log('e', req.headers);
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function _getResponseEntity(res) {
|
|
45
|
+
// If body is not defined:
|
|
46
|
+
const { body } = res;
|
|
47
|
+
if (!body || res.get('etag'))
|
|
48
|
+
return;
|
|
49
|
+
|
|
50
|
+
// Status code.
|
|
51
|
+
const status = res.statusCode / 100 | 0;
|
|
52
|
+
|
|
53
|
+
console.log('getResponseEntity', status, { tag: res.get('etag') });
|
|
54
|
+
|
|
55
|
+
// 2xx
|
|
56
|
+
if (status !== 2)
|
|
57
|
+
return;
|
|
58
|
+
|
|
59
|
+
if (body instanceof Stream) {
|
|
60
|
+
if (!body.path)
|
|
61
|
+
return;
|
|
62
|
+
|
|
63
|
+
const stats = await getFileStats(body.path);
|
|
64
|
+
return stats;
|
|
65
|
+
}
|
|
66
|
+
else if (typeof body === 'string' || Buffer.isBuffer(body)) {
|
|
67
|
+
return body;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return JSON.stringify(body);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _setEtag(res, entity, options) {
|
|
75
|
+
console.log('setEtag', typeof res, entity);
|
|
76
|
+
|
|
77
|
+
if (!entity)
|
|
78
|
+
return;
|
|
79
|
+
|
|
80
|
+
res.etag = calculate(entity, options);
|
|
81
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
module.exports = function initRenderMiddleware() {
|
|
10
|
+
return handle;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
function handle(req, res, next) {
|
|
15
|
+
const context = { req, res, next };
|
|
16
|
+
res.render = _render.bind(context);
|
|
17
|
+
next();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Render `view` with the given `options` and optional callback `fn`.
|
|
23
|
+
* When a callback function is given a response will _not_ be made
|
|
24
|
+
* automatically, otherwise a response of _200_ and _text/html_ is given.
|
|
25
|
+
*
|
|
26
|
+
* Options:
|
|
27
|
+
*
|
|
28
|
+
* - `cache` boolean hinting to the engine it should cache
|
|
29
|
+
* - `filename` filename of the view being rendered
|
|
30
|
+
*
|
|
31
|
+
* @alias render
|
|
32
|
+
* @access public
|
|
33
|
+
*/
|
|
34
|
+
function _render(view, options, callback) {
|
|
35
|
+
const app = this.req.app;
|
|
36
|
+
let done = callback;
|
|
37
|
+
let opts = options || {};
|
|
38
|
+
|
|
39
|
+
const req = this.req;
|
|
40
|
+
const res = this.res;
|
|
41
|
+
const next = this.next;
|
|
42
|
+
|
|
43
|
+
// support callback function as second arg
|
|
44
|
+
if (typeof options === 'function') {
|
|
45
|
+
done = options;
|
|
46
|
+
opts = {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// merge res.locals
|
|
50
|
+
opts._locals = res.locals;
|
|
51
|
+
|
|
52
|
+
// default callback to respond
|
|
53
|
+
done = done || function (err, str) {
|
|
54
|
+
if (err)
|
|
55
|
+
return next(err);
|
|
56
|
+
|
|
57
|
+
res.send(str);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// render
|
|
61
|
+
app.render(view, opts, done);
|
|
62
|
+
};
|
package/lib/models/mixins.js
CHANGED
|
@@ -212,10 +212,8 @@ async function _updateOne(
|
|
|
212
212
|
|
|
213
213
|
// Will contain data from parent instance and associations.
|
|
214
214
|
const fullInstanceData = instance.toJSON();
|
|
215
|
+
const parentData = { ...data };
|
|
215
216
|
|
|
216
|
-
const parentData = {
|
|
217
|
-
...data
|
|
218
|
-
}
|
|
219
217
|
for (let includeConfig of include) {
|
|
220
218
|
const { association } = includeConfig;
|
|
221
219
|
|
|
@@ -223,6 +221,7 @@ async function _updateOne(
|
|
|
223
221
|
continue;
|
|
224
222
|
}
|
|
225
223
|
|
|
224
|
+
// Remove association from parentData (handled separately)
|
|
226
225
|
delete parentData[association];
|
|
227
226
|
|
|
228
227
|
const associationDefinition = this.associations[association];
|
|
@@ -246,6 +245,15 @@ async function _updateOne(
|
|
|
246
245
|
// If association type is HasMany or HasOne (We don't work with any other):
|
|
247
246
|
switch(associationType) {
|
|
248
247
|
case 'HasMany': {
|
|
248
|
+
// Handle empty array (remove all old associations):
|
|
249
|
+
if (Array.isArray(includeData) && includeData.length === 0) {
|
|
250
|
+
const where = {
|
|
251
|
+
[foreignKey]: instance.id
|
|
252
|
+
}
|
|
253
|
+
await associatedModel.destroy({ where });
|
|
254
|
+
fullInstanceData[association] = [];
|
|
255
|
+
}
|
|
256
|
+
|
|
249
257
|
const promises = includeData.map(singleData => {
|
|
250
258
|
// Note: for now we are only able to work with a model with single PrimaryKey:
|
|
251
259
|
const where = {
|
|
@@ -263,16 +271,25 @@ async function _updateOne(
|
|
|
263
271
|
}
|
|
264
272
|
|
|
265
273
|
case 'HasOne': {
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
274
|
+
// Handle null case (remove old association)
|
|
275
|
+
if (includeData === null) {
|
|
276
|
+
const where = {
|
|
277
|
+
[foreignKey]: instance.id
|
|
278
|
+
}
|
|
279
|
+
await associatedModel.destroy({ where });
|
|
280
|
+
fullInstanceData[association] = null;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
// Note: for now we are only able to work with a model with single PrimaryKey:
|
|
284
|
+
const where = {
|
|
285
|
+
[pkField]: includeData[pkField]
|
|
286
|
+
}
|
|
287
|
+
fullInstanceData[association] = await associatedModel.updateOne(
|
|
288
|
+
where,
|
|
289
|
+
includeData,
|
|
290
|
+
associationUpdateOpts
|
|
291
|
+
);
|
|
269
292
|
}
|
|
270
|
-
fullInstanceData[association] = await associatedModel.updateOne(
|
|
271
|
-
where,
|
|
272
|
-
includeData,
|
|
273
|
-
associationUpdateOpts
|
|
274
|
-
);
|
|
275
|
-
|
|
276
293
|
continue;
|
|
277
294
|
}
|
|
278
295
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodester",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "A versatile REST framework for Node.js",
|
|
5
5
|
"directories": {
|
|
6
6
|
"docs": "docs",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"http-errors": "^2.0.0",
|
|
90
90
|
"inflection": "^2.0.1",
|
|
91
91
|
"mime": "^3.0.0",
|
|
92
|
-
"mysql2": "^3.
|
|
93
|
-
"pg": "^8.
|
|
92
|
+
"mysql2": "^3.14.4",
|
|
93
|
+
"pg": "^8.16.3",
|
|
94
94
|
"pg-hstore": "^2.3.4",
|
|
95
95
|
"qs": "^6.11.0",
|
|
96
96
|
"sequelize": "^6.6.5"
|
package/tests/ast.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { ModelsTree } = require('../lib/middlewares/ql/sequelize/interpreter/ModelsTree');
|
|
2
|
+
const { AST_ModelsTree } = require('../lib/tools/nql.tool');
|
|
3
|
+
|
|
4
|
+
const tree = new ModelsTree();
|
|
5
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
6
|
+
tree.include('comments').use('comments');
|
|
7
|
+
tree.node.order = 'desc';
|
|
8
|
+
tree.up();
|
|
9
|
+
tree.include('users');
|
|
10
|
+
tree.include('likes') && tree.use('likes');
|
|
11
|
+
tree.node.order = 'rand';
|
|
12
|
+
tree.up();
|
|
13
|
+
tree.include('reposts');
|
|
14
|
+
|
|
15
|
+
console.debug(
|
|
16
|
+
AST_ModelsTree(tree)
|
|
17
|
+
);
|
|
18
|
+
|