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
package/lib/models/mixins.js
CHANGED
|
@@ -6,35 +6,50 @@ const {
|
|
|
6
6
|
modelHasAssociations,
|
|
7
7
|
getModelAssociationProps,
|
|
8
8
|
compileModelAssociationData,
|
|
9
|
-
} = require('
|
|
9
|
+
} = require('../utils/modelAssociations.util');
|
|
10
|
+
|
|
11
|
+
// Nodester query:
|
|
12
|
+
const NQLexer = require('../middlewares/ql/sequelize/interpreter/QueryLexer');
|
|
13
|
+
const traverseNQuery = require('../queries/traverse');
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
module.exports = {
|
|
13
17
|
implementsCRUD: _implementsCRUD
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
function _implementsCRUD(modelDefinition) {
|
|
17
|
-
if (!!modelDefinition) {
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Sets all of CRUD methods to Model.
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} modelDefinition
|
|
25
|
+
*
|
|
26
|
+
*
|
|
27
|
+
* @api public
|
|
28
|
+
* @alias implementsCRUD
|
|
29
|
+
*/
|
|
30
|
+
function _implementsCRUD(modelDefinition) {
|
|
31
|
+
if (!modelDefinition) {
|
|
32
|
+
const err = new TypeError(`"modelDefinition" argument is not provided.`);
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
modelDefinition.findOneWithIncludes = _findOneWithIncludes.bind(modelDefinition);
|
|
36
|
+
// Create.
|
|
37
|
+
modelDefinition.createWithIncludes = _createWithIncludes.bind(modelDefinition);
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
// Read:
|
|
40
|
+
modelDefinition.findById = _findById.bind(modelDefinition);
|
|
41
|
+
modelDefinition.findMany = _findMany.bind(modelDefinition);
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
// Update:
|
|
44
|
+
modelDefinition.updateOne = _updateOne.bind(modelDefinition);
|
|
45
|
+
modelDefinition.updateById = _updateById.bind(modelDefinition);
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
// Delete:
|
|
48
|
+
modelDefinition.deleteOne = _deleteOne.bind(modelDefinition);
|
|
49
|
+
modelDefinition.deleteById = _deleteById.bind(modelDefinition);
|
|
36
50
|
}
|
|
37
51
|
|
|
52
|
+
|
|
38
53
|
/* Main mixinis: */
|
|
39
54
|
async function _createWithIncludes(
|
|
40
55
|
data={}
|
|
@@ -101,37 +116,76 @@ async function _createWithIncludes(
|
|
|
101
116
|
|
|
102
117
|
function _findById(
|
|
103
118
|
id=null,
|
|
104
|
-
|
|
105
|
-
paranoid=true
|
|
119
|
+
opts={}
|
|
106
120
|
) {
|
|
107
|
-
const query =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
const { query } = opts;
|
|
122
|
+
|
|
123
|
+
let _query = {};
|
|
124
|
+
|
|
125
|
+
if (typeof query === 'string') {
|
|
126
|
+
const lexer = new NQLexer(query);
|
|
127
|
+
const nquery = lexer.query;
|
|
128
|
+
_query = traverseNQuery(nquery, null, this);
|
|
129
|
+
_query.where = {
|
|
130
|
+
..._query.where,
|
|
131
|
+
id: id
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const {
|
|
136
|
+
include,
|
|
137
|
+
paranoid
|
|
138
|
+
} = opts;
|
|
139
|
+
|
|
140
|
+
_query = {
|
|
141
|
+
where: { id },
|
|
142
|
+
include: include,
|
|
143
|
+
paranoid: !!paranoid
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return this.findOne(_query);
|
|
113
148
|
}
|
|
114
149
|
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
150
|
+
function _findMany(opts={}) {
|
|
151
|
+
const { query } = opts;
|
|
152
|
+
|
|
153
|
+
let _query = {};
|
|
154
|
+
|
|
155
|
+
if (typeof query === 'string') {
|
|
156
|
+
const lexer = new NQLexer(query);
|
|
157
|
+
const nquery = lexer.query;
|
|
158
|
+
_query = traverseNQuery(nquery, null, this);
|
|
159
|
+
_query.where = {
|
|
160
|
+
..._query.where,
|
|
161
|
+
id: id
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const {
|
|
166
|
+
include,
|
|
167
|
+
paranoid
|
|
168
|
+
} = opts;
|
|
169
|
+
|
|
170
|
+
_query = {
|
|
171
|
+
where: { id },
|
|
172
|
+
include: include,
|
|
173
|
+
paranoid: !!paranoid
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
return this.findAll(_query);
|
|
126
179
|
}
|
|
127
180
|
|
|
181
|
+
|
|
128
182
|
async function _updateOne(
|
|
129
183
|
where,
|
|
130
184
|
data,
|
|
131
185
|
include=[]
|
|
132
186
|
) {
|
|
133
187
|
try {
|
|
134
|
-
const instance = await this.
|
|
188
|
+
const instance = await this.findOne({ where, include });
|
|
135
189
|
|
|
136
190
|
if (!instance) {
|
|
137
191
|
const err = new Error(`Model not found`);
|
|
@@ -188,7 +242,7 @@ async function _updateOne(
|
|
|
188
242
|
|
|
189
243
|
// Select this instance again, if includes was set:
|
|
190
244
|
if (include?.length > 0) {
|
|
191
|
-
const updatedInstance = await this.
|
|
245
|
+
const updatedInstance = await this.findOne({ where, include });
|
|
192
246
|
fullInstanceData = updatedInstance.toJSON();
|
|
193
247
|
}
|
|
194
248
|
|
|
@@ -212,6 +266,15 @@ async function _updateById(
|
|
|
212
266
|
);
|
|
213
267
|
}
|
|
214
268
|
|
|
269
|
+
|
|
270
|
+
function _deleteOne(query={}) {
|
|
271
|
+
const _query = {
|
|
272
|
+
...query,
|
|
273
|
+
limit: 1
|
|
274
|
+
}
|
|
275
|
+
return this.destroy(_query);
|
|
276
|
+
}
|
|
277
|
+
|
|
215
278
|
function _deleteById(
|
|
216
279
|
id=null
|
|
217
280
|
) {
|
|
@@ -222,47 +285,6 @@ function _deleteById(
|
|
|
222
285
|
}
|
|
223
286
|
/* Main mixinis\ */
|
|
224
287
|
|
|
225
|
-
/* Association mixins: */
|
|
226
|
-
function _getIncludesList(facadeData=null) {
|
|
227
|
-
// console.log({ facadeData, model: this });
|
|
228
|
-
|
|
229
|
-
const result = [];
|
|
230
|
-
|
|
231
|
-
const associations = this.associations;
|
|
232
|
-
const associationEntries = Object.entries(associations);
|
|
233
|
-
|
|
234
|
-
associationEntries.forEach(([
|
|
235
|
-
associationName,
|
|
236
|
-
associationDefinition
|
|
237
|
-
]) => {
|
|
238
|
-
const a = { association: associationName };
|
|
239
|
-
|
|
240
|
-
if (!!facadeData) {
|
|
241
|
-
// If facade data is set, go deeper:
|
|
242
|
-
const keys = Object.keys( facadeData );
|
|
243
|
-
if (keys.indexOf(associationName) > 0) {
|
|
244
|
-
const associationModel = associationDefinition.target;
|
|
245
|
-
|
|
246
|
-
const a = { association: associationName };
|
|
247
|
-
if (Object.entries(associationModel.associations).length > 0) {
|
|
248
|
-
const deepData = facadeData[ associationName ];
|
|
249
|
-
a.include = associationModel.getIncludesList(Array.isArray(deepData) ? deepData[0] : deepData);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
result.push( a );
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
result.push( a );
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// console.log('getIncludesList', result);
|
|
261
|
-
|
|
262
|
-
return result;
|
|
263
|
-
}
|
|
264
|
-
/* Association mixins\ */
|
|
265
|
-
|
|
266
288
|
/* Subfunctions: */
|
|
267
289
|
async function _updateOrCreateOrDelete(
|
|
268
290
|
modelDefinition,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = Params;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extracts only values in second argument
|
|
6
|
+
* from first argument.
|
|
7
|
+
* If such values is missing in first atgument,
|
|
8
|
+
* will fallback to the value in second argument.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} sourceObj
|
|
11
|
+
* @param {Object} defaultValuesList
|
|
12
|
+
*
|
|
13
|
+
* @return {Object} result
|
|
14
|
+
*
|
|
15
|
+
* @api public
|
|
16
|
+
*/
|
|
17
|
+
function Params(
|
|
18
|
+
sourceObj={},
|
|
19
|
+
defaultValuesList={}
|
|
20
|
+
) {
|
|
21
|
+
const result = {};
|
|
22
|
+
|
|
23
|
+
const keys = Object.keys(defaultValuesList);
|
|
24
|
+
for (const key of keys) {
|
|
25
|
+
|
|
26
|
+
// If value is not set,
|
|
27
|
+
// use default one from 'defaultValuesList':
|
|
28
|
+
if (sourceObj[key] === undefined) {
|
|
29
|
+
result[key] = defaultValuesList[key];
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
result[key] = sourceObj[key];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const CLAUSES = ['limit', 'skip', 'order', 'order_by'];
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module.exports = class Colander {
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
*
|
|
8
|
+
* @param {Object|Model} optsOrModelDefinition
|
|
9
|
+
* - @param {Array} fields
|
|
10
|
+
* - @param {Array} clauses
|
|
11
|
+
* - @param {Object} includes
|
|
12
|
+
* - @param {Object} statics
|
|
13
|
+
* -- @param {Object} attributes
|
|
14
|
+
* -- @param {Object} clauses
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
constructor(optsOrModelDefinition) {
|
|
18
|
+
this._fields = [];
|
|
19
|
+
this._clauses = [];
|
|
20
|
+
this._includes = {};
|
|
21
|
+
|
|
22
|
+
this._statics = {
|
|
23
|
+
attributes: {},
|
|
24
|
+
clauses: {
|
|
25
|
+
limit: 3
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// If model:
|
|
30
|
+
if (!!optsOrModelDefinition.tableName && typeof optsOrModelDefinition._schema === 'object') {
|
|
31
|
+
this._fields = Object.keys(optsOrModelDefinition.tableAttributes);
|
|
32
|
+
this._clauses = CLAUSES;
|
|
33
|
+
}
|
|
34
|
+
// If options:
|
|
35
|
+
else {
|
|
36
|
+
const {
|
|
37
|
+
fields,
|
|
38
|
+
clauses,
|
|
39
|
+
includes,
|
|
40
|
+
statics,
|
|
41
|
+
} = optsOrModelDefinition;
|
|
42
|
+
|
|
43
|
+
if (Array.isArray(fields)) {
|
|
44
|
+
this._fields = fields;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (Array.isArray(clauses)) {
|
|
48
|
+
this._clauses = clauses;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof includes === 'object') {
|
|
52
|
+
this._includes = includes;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof statics === 'object') {
|
|
56
|
+
if (typeof statics.attributes === 'object') {
|
|
57
|
+
this._statics.attributes = statics.attributes;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof statics.clauses === 'object') {
|
|
61
|
+
this._statics.clauses = statics.clauses;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Getters:
|
|
68
|
+
get fields() {
|
|
69
|
+
return this._fields;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get clauses() {
|
|
73
|
+
return this._clauses;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get includes() {
|
|
77
|
+
return this._includes;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get statics() {
|
|
81
|
+
return this._statics;
|
|
82
|
+
}
|
|
83
|
+
// Getters\
|
|
84
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Dictionary of unsafe characters:
|
|
2
|
+
const NOT_ALLOWED = [
|
|
3
|
+
'{',
|
|
4
|
+
'}',
|
|
5
|
+
// `\`,
|
|
6
|
+
'^',
|
|
7
|
+
'~',
|
|
8
|
+
'[',
|
|
9
|
+
']',
|
|
10
|
+
'`'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const util = require('util');
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* NodesterQueryParams is a ready-to-use replacement for URLSearchParams.
|
|
17
|
+
* The only difference is that NodesterQueryParams
|
|
18
|
+
* respects nested "&" during parsing.
|
|
19
|
+
*/
|
|
20
|
+
module.exports = class NodesterQueryParams {
|
|
21
|
+
constructor(queryString='') {
|
|
22
|
+
// Type validateion:
|
|
23
|
+
if (typeof queryString !== 'string') {
|
|
24
|
+
const err = new TypeError(`'query' must be a String.`);
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// You never know if it's encoded or not.
|
|
29
|
+
const decoded = decodeURI(queryString);
|
|
30
|
+
|
|
31
|
+
// Indicates, how deep the char is inside different ().
|
|
32
|
+
let deep = 0;
|
|
33
|
+
|
|
34
|
+
const paramLevels = {};
|
|
35
|
+
|
|
36
|
+
// Current query parameter.
|
|
37
|
+
let param = '';
|
|
38
|
+
|
|
39
|
+
// Current query token.
|
|
40
|
+
let token = '';
|
|
41
|
+
|
|
42
|
+
this._map = new Map();
|
|
43
|
+
|
|
44
|
+
for (let i=0; i < decoded.length; i++) {
|
|
45
|
+
const char = decoded[i];
|
|
46
|
+
|
|
47
|
+
// Validate char:
|
|
48
|
+
if (NOT_ALLOWED.indexOf(char) > -1) {
|
|
49
|
+
const err = new TypeError(`Invalid query token at ${ i }: '${ char }'`);
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (char === '(') {
|
|
54
|
+
// Error If there is nothing behind:
|
|
55
|
+
if (param.length === 0) {
|
|
56
|
+
const err = new TypeError(`Invalid query token at ${ i }: '${ char }'`);
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// If not special token, go deeper:
|
|
61
|
+
if (['and', 'or', 'xor', 'not', '!', '|', 'like'].indexOf(token) === -1) {
|
|
62
|
+
this.append(param, token);
|
|
63
|
+
deep++;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// will set ( in token later.
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (char === ')') {
|
|
70
|
+
// If sub-level:
|
|
71
|
+
if (deep > 0) {
|
|
72
|
+
deep--
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// & can mean the end of key=value pair:
|
|
77
|
+
if (char === '&') {
|
|
78
|
+
// If top-level:
|
|
79
|
+
if (deep === 0) {
|
|
80
|
+
this.append(param, token);
|
|
81
|
+
param = '';
|
|
82
|
+
token = '';
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// If sub-level do nothing.
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// = can mean the end of param name:
|
|
90
|
+
if (char === '=') {
|
|
91
|
+
// If top-level:
|
|
92
|
+
if (deep === 0) {
|
|
93
|
+
param = token;
|
|
94
|
+
token = '';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Continue building token:
|
|
99
|
+
if (char !== '=' || deep > 0 ) {
|
|
100
|
+
token += char;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If last char:
|
|
104
|
+
if (i === decoded.length-1) {
|
|
105
|
+
// Validate:
|
|
106
|
+
if (deep > 0) {
|
|
107
|
+
const err = new TypeError(`Missing ')' at ${ i }`);
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.append(param, token);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
append(...args) {
|
|
117
|
+
return this._map.set(...args);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get(...args) {
|
|
121
|
+
return this._map.get(...args);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
delete(...args) {
|
|
125
|
+
return this._map.delete(...args);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
entries(...args) {
|
|
129
|
+
return this._map.entries(...args);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
toString() {
|
|
133
|
+
return this._map.toString();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[util.inspect.custom](depth, opts) {
|
|
137
|
+
return this._map;
|
|
138
|
+
}
|
|
139
|
+
}
|