nodester 0.3.4 → 0.4.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/lib/body/extract.js +18 -3
- package/lib/controllers/mixins/index.js +5 -5
- package/lib/facades/methods/index.js +26 -8
- package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +3 -2
- package/lib/models/mixins.js +87 -50
- package/lib/query/{traverse.js → traverse/index.js} +60 -55
- package/lib/query/traverse/parsers.js +41 -0
- package/lib/query/traverse/utils.js +40 -0
- package/lib/utils/modelAssociations.util.js +18 -4
- package/lib/utils/sanitizations.util.js +53 -25
- package/package.json +2 -2
package/lib/body/extract.js
CHANGED
|
@@ -65,17 +65,32 @@ function extract(body, filter=null, model) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
if (isInclude) {
|
|
68
|
-
const
|
|
68
|
+
const availableIncludeFilters = Object.keys(filter.includes);
|
|
69
69
|
|
|
70
|
-
if (
|
|
70
|
+
if (availableIncludeFilters.indexOf(key) === -1) {
|
|
71
71
|
const err = new Error(`Include '${ key }' is not available.`);
|
|
72
72
|
err.status = httpCodes.NOT_ACCEPTABLE;
|
|
73
73
|
throw err;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
const thisIncludeFilter = filter.includes[key];
|
|
77
|
+
|
|
76
78
|
const association = model.associations[key];
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
// Mutliple includes:
|
|
81
|
+
if (Array.isArray(value)) {
|
|
82
|
+
filteredBody[key] = [];
|
|
83
|
+
|
|
84
|
+
for (let thisIncludeBody of value) {
|
|
85
|
+
filteredBody[key].push(
|
|
86
|
+
extract(thisIncludeBody, thisIncludeFilter, association.target)
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const thisIncludeBody = value;
|
|
92
|
+
filteredBody[key] = extract(thisIncludeBody, thisIncludeFilter, association.target);
|
|
93
|
+
}
|
|
79
94
|
|
|
80
95
|
continue;
|
|
81
96
|
}
|
|
@@ -151,12 +151,12 @@ function _withDefaultErrorProcessing(controller, options={}) {
|
|
|
151
151
|
switch(error.name) {
|
|
152
152
|
case UNAUTHORIZED_ERROR: {
|
|
153
153
|
statusCode = 401;
|
|
154
|
-
errorResponse.details = { message: 'Unauthorized' };
|
|
154
|
+
errorResponse.details = error?.details ?? { message: 'Unauthorized' };
|
|
155
155
|
break;
|
|
156
156
|
}
|
|
157
157
|
case NOT_FOUND_ERROR: {
|
|
158
158
|
statusCode = 404;
|
|
159
|
-
errorResponse.details = { message: errorMessage };
|
|
159
|
+
errorResponse.details = error?.details ?? { message: errorMessage };
|
|
160
160
|
break;
|
|
161
161
|
}
|
|
162
162
|
case NODESTER_QUERY_ERROR: {
|
|
@@ -166,7 +166,7 @@ function _withDefaultErrorProcessing(controller, options={}) {
|
|
|
166
166
|
}
|
|
167
167
|
case VALIDATION_ERROR: {
|
|
168
168
|
statusCode = 422;
|
|
169
|
-
errorResponse.details = error?.details;
|
|
169
|
+
errorResponse.details = error?.details ?? error?.details;
|
|
170
170
|
break;
|
|
171
171
|
}
|
|
172
172
|
case CONFLICT_ERROR: {
|
|
@@ -181,11 +181,11 @@ function _withDefaultErrorProcessing(controller, options={}) {
|
|
|
181
181
|
}
|
|
182
182
|
case INTERNAL_VALIDATION_ERROR: {
|
|
183
183
|
statusCode = 500;
|
|
184
|
-
errorResponse.details = { message: errorMessage };
|
|
184
|
+
errorResponse.details = error?.details ?? { message: errorMessage };
|
|
185
185
|
break;
|
|
186
186
|
}
|
|
187
187
|
default: {
|
|
188
|
-
errorResponse.details = { message: errorMessage };
|
|
188
|
+
errorResponse.details = error?.details ?? { message: errorMessage };
|
|
189
189
|
break;
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
// Constants:
|
|
9
|
+
const HTTP_CODES = require('nodester/http/codes');
|
|
10
|
+
const HTTP_CODE_DESCRIPTIONS = require('nodester/http/codes/descriptions');
|
|
11
|
+
|
|
8
12
|
const Params = require('nodester/params');
|
|
9
13
|
|
|
10
14
|
const log = require('nodester/loggers/dev');
|
|
@@ -106,8 +110,8 @@ async function _createOne(params) {
|
|
|
106
110
|
});
|
|
107
111
|
|
|
108
112
|
const instance = await this.model.create({ ...data }, {
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
include: this.model.getIncludesTree(data)
|
|
114
|
+
});
|
|
111
115
|
|
|
112
116
|
const result = {
|
|
113
117
|
[this.outputName.singular]: instance,
|
|
@@ -145,12 +149,11 @@ async function _updateOne(params) {
|
|
|
145
149
|
data: null
|
|
146
150
|
});
|
|
147
151
|
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
const instance = await this.model.updateOne(query.where, data, {
|
|
153
|
+
include: this.model.getIncludesTree(data)
|
|
154
|
+
});
|
|
151
155
|
|
|
152
156
|
const result = {
|
|
153
|
-
success: isNewRecord === false,
|
|
154
157
|
[this.outputName.singular]: instance,
|
|
155
158
|
count: 0 + (instance !== null)
|
|
156
159
|
}
|
|
@@ -185,8 +188,18 @@ async function _deleteOne(params) {
|
|
|
185
188
|
|
|
186
189
|
const count = await this.model.deleteOne(query);
|
|
187
190
|
|
|
191
|
+
// Model was not found:
|
|
192
|
+
if (count === 0) {
|
|
193
|
+
const err = new Error(HTTP_CODE_DESCRIPTIONS.NOT_FOUND);
|
|
194
|
+
err.statusCode = HTTP_CODES.NOT_FOUND;
|
|
195
|
+
err.details = {
|
|
196
|
+
message: 'Resource not found. Nothing was deleted.'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
|
|
188
202
|
const result = {
|
|
189
|
-
success: count > 0,
|
|
190
203
|
count: count
|
|
191
204
|
};
|
|
192
205
|
|
|
@@ -196,7 +209,12 @@ async function _deleteOne(params) {
|
|
|
196
209
|
return Promise.resolve(result);
|
|
197
210
|
}
|
|
198
211
|
catch(error) {
|
|
199
|
-
|
|
212
|
+
|
|
213
|
+
// 404 will not be logged:
|
|
214
|
+
if (error.statusCode !== HTTP_CODES.NOT_FOUND) {
|
|
215
|
+
log.error(`${ this.name }.deleteOne error:`, error);
|
|
216
|
+
}
|
|
217
|
+
|
|
200
218
|
return Promise.reject(error);
|
|
201
219
|
}
|
|
202
220
|
}
|
|
@@ -29,8 +29,9 @@ class ModelsTreeNode {
|
|
|
29
29
|
this.limit = -1; // No limit
|
|
30
30
|
|
|
31
31
|
this._includes = opts.includes ?? [];
|
|
32
|
-
|
|
33
|
-
this.
|
|
32
|
+
|
|
33
|
+
this.order = opts.order ?? undefined;
|
|
34
|
+
this.order_by = opts.order_by ?? undefined;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
// Getters:
|
package/lib/models/mixins.js
CHANGED
|
@@ -86,7 +86,7 @@ async function _createWithIncludes(
|
|
|
86
86
|
associatedModel,
|
|
87
87
|
foreignKey,
|
|
88
88
|
associationType
|
|
89
|
-
} = getModelAssociationProps(associationDefinition
|
|
89
|
+
} = getModelAssociationProps(associationDefinition);
|
|
90
90
|
|
|
91
91
|
// If association type is HasMany or HasOne (We don't work with any other):
|
|
92
92
|
if (associationType === 'HasMany' || associationType === 'HasOne') {
|
|
@@ -189,71 +189,108 @@ function _findMany(opts={}) {
|
|
|
189
189
|
async function _updateOne(
|
|
190
190
|
where,
|
|
191
191
|
data,
|
|
192
|
-
|
|
192
|
+
opts={}
|
|
193
193
|
) {
|
|
194
194
|
try {
|
|
195
|
-
const
|
|
195
|
+
const include = opts.include ?? [];
|
|
196
|
+
let instance = await this.findOne({ where, include });
|
|
197
|
+
let isNewRecord = false;
|
|
196
198
|
|
|
197
199
|
if (!instance) {
|
|
198
|
-
const err = new Error(`Model not found`);
|
|
199
|
-
err.name = 'NotFound';
|
|
200
|
-
throw err;
|
|
201
|
-
}
|
|
202
200
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
201
|
+
if (opts.findOrCreate === true) {
|
|
202
|
+
instance = await this.create(data);
|
|
203
|
+
isNewRecord = true;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
const err = new Error(`Model not found`);
|
|
207
|
+
err.name = 'NotFound';
|
|
208
|
+
throw err;
|
|
209
|
+
}
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
if (modelHasAssociations(this)) {
|
|
211
|
+
}
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
// Will contain data from parent instance and associations.
|
|
214
|
+
const fullInstanceData = instance.toJSON();
|
|
216
215
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
associatedModel,
|
|
223
|
-
foreignKey,
|
|
224
|
-
associationType
|
|
225
|
-
} = getModelAssociationProps(associationDefinition, data);
|
|
216
|
+
const parentData = {
|
|
217
|
+
...data
|
|
218
|
+
}
|
|
219
|
+
for (let includeConfig of include) {
|
|
220
|
+
const { association } = includeConfig;
|
|
226
221
|
|
|
227
|
-
|
|
228
|
-
|
|
222
|
+
if (parentData[association] === undefined) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
229
225
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
226
|
+
delete parentData[association];
|
|
227
|
+
|
|
228
|
+
const associationDefinition = this.associations[association];
|
|
229
|
+
const includeData = data[association];
|
|
230
|
+
|
|
231
|
+
const {
|
|
232
|
+
associatedModel,
|
|
233
|
+
associationType,
|
|
234
|
+
|
|
235
|
+
foreignKey,
|
|
236
|
+
sourceKey
|
|
237
|
+
} = getModelAssociationProps(associationDefinition);
|
|
238
|
+
|
|
239
|
+
const associationUpdateOpts = {
|
|
240
|
+
findOrCreate: true,
|
|
241
|
+
include: associatedModel.getIncludesTree(includeData)
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const pkField = associatedModel.primaryKeyField;
|
|
245
|
+
|
|
246
|
+
// If association type is HasMany or HasOne (We don't work with any other):
|
|
247
|
+
switch(associationType) {
|
|
248
|
+
case 'HasMany': {
|
|
249
|
+
const promises = includeData.map(singleData => {
|
|
250
|
+
// Note: for now we are only able to work with a model with single PrimaryKey:
|
|
251
|
+
const where = {
|
|
252
|
+
[pkField]: singleData[pkField]
|
|
253
|
+
}
|
|
254
|
+
return associatedModel.updateOne(
|
|
255
|
+
where,
|
|
256
|
+
singleData,
|
|
257
|
+
associationUpdateOpts
|
|
258
|
+
)
|
|
259
|
+
});
|
|
260
|
+
fullInstanceData[association] = await Promise.all(promises);
|
|
261
|
+
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
237
264
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
265
|
+
case 'HasOne': {
|
|
266
|
+
// Note: for now we are only able to work with a model with single PrimaryKey:
|
|
267
|
+
const where = {
|
|
268
|
+
[pkField]: includeData[pkField]
|
|
242
269
|
}
|
|
270
|
+
fullInstanceData[association] = await associatedModel.updateOne(
|
|
271
|
+
where,
|
|
272
|
+
includeData,
|
|
273
|
+
associationUpdateOpts
|
|
274
|
+
);
|
|
243
275
|
|
|
244
|
-
|
|
276
|
+
continue;
|
|
245
277
|
}
|
|
246
|
-
}
|
|
247
278
|
|
|
279
|
+
default:
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
248
282
|
}
|
|
249
|
-
|
|
250
|
-
//
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
283
|
+
|
|
284
|
+
// Update parent model instance:
|
|
285
|
+
if (isNewRecord === false) {
|
|
286
|
+
instance.set(parentData);
|
|
287
|
+
const saveResult = await instance.save();
|
|
254
288
|
}
|
|
255
289
|
|
|
256
|
-
return Promise.resolve(
|
|
290
|
+
return Promise.resolve({
|
|
291
|
+
_is_new_record: isNewRecord,
|
|
292
|
+
...fullInstanceData
|
|
293
|
+
});
|
|
257
294
|
}
|
|
258
295
|
catch(error) {
|
|
259
296
|
return Promise.reject(error);
|
|
@@ -263,13 +300,13 @@ async function _updateOne(
|
|
|
263
300
|
async function _updateById(
|
|
264
301
|
id=null,
|
|
265
302
|
data={},
|
|
266
|
-
|
|
303
|
+
opts={}
|
|
267
304
|
) {
|
|
268
305
|
const where = { id };
|
|
269
306
|
return this.updateOne(
|
|
270
307
|
where,
|
|
271
308
|
data,
|
|
272
|
-
|
|
309
|
+
opts
|
|
273
310
|
);
|
|
274
311
|
}
|
|
275
312
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const BOUNDS = require('
|
|
8
|
+
const BOUNDS = require('../../constants/Bounds');
|
|
9
9
|
|
|
10
10
|
const { Op } = require('sequelize');
|
|
11
11
|
const { NodesterQueryError } = require('nodester/errors');
|
|
@@ -13,6 +13,20 @@ const httpCodes = require('nodester/http/codes');
|
|
|
13
13
|
|
|
14
14
|
const { ensure } = require('nodester/validators/arguments');
|
|
15
15
|
|
|
16
|
+
const {
|
|
17
|
+
parseValue,
|
|
18
|
+
parseWhereEntry,
|
|
19
|
+
} = require('./parsers');
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
disassembleQueryNode,
|
|
23
|
+
addAssociationQuery,
|
|
24
|
+
} = require('./utils');
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
getModelAssociationProps
|
|
28
|
+
} = require('../../utils/modelAssociations.util');
|
|
29
|
+
|
|
16
30
|
|
|
17
31
|
module.exports = traverse;
|
|
18
32
|
|
|
@@ -58,7 +72,7 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
58
72
|
where,
|
|
59
73
|
|
|
60
74
|
includes,
|
|
61
|
-
} =
|
|
75
|
+
} = disassembleQueryNode(queryNode);
|
|
62
76
|
|
|
63
77
|
|
|
64
78
|
// Attribute:
|
|
@@ -200,10 +214,16 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
200
214
|
continue;
|
|
201
215
|
}
|
|
202
216
|
case 'order':
|
|
217
|
+
if (value === undefined)
|
|
218
|
+
continue;
|
|
219
|
+
|
|
203
220
|
order.order = value;
|
|
204
221
|
continue;
|
|
205
222
|
|
|
206
223
|
case 'order_by':
|
|
224
|
+
if (value === undefined)
|
|
225
|
+
continue;
|
|
226
|
+
|
|
207
227
|
order.by = value;
|
|
208
228
|
continue;
|
|
209
229
|
|
|
@@ -303,13 +323,13 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
303
323
|
// Set aatributes from Query:
|
|
304
324
|
const whereEntries = Object.entries(where);
|
|
305
325
|
for (let [ attribute, value ] of whereEntries) {
|
|
306
|
-
|
|
326
|
+
parseWhereEntry(attribute, value, newQuery.where);
|
|
307
327
|
}
|
|
308
328
|
|
|
309
329
|
// Static attributes override previously set attributes:
|
|
310
330
|
const staticAttributesEntries = Object.entries(filter.statics.attributes);
|
|
311
331
|
for (let [ attribute, staticValue ] of staticAttributesEntries) {
|
|
312
|
-
newQuery.where[attribute] =
|
|
332
|
+
newQuery.where[attribute] = parseValue(staticValue, attribute);
|
|
313
333
|
}
|
|
314
334
|
|
|
315
335
|
// If "where" was not set in any way,
|
|
@@ -319,6 +339,12 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
319
339
|
}
|
|
320
340
|
// Where\
|
|
321
341
|
|
|
342
|
+
// Combine included orders into one at the top level:
|
|
343
|
+
// - Why?
|
|
344
|
+
// - Sequelize ingores included orders for association types like:
|
|
345
|
+
// • HasMany
|
|
346
|
+
_traverseIncludedOrders(newQuery, _model);
|
|
347
|
+
|
|
322
348
|
return newQuery;
|
|
323
349
|
}
|
|
324
350
|
|
|
@@ -355,66 +381,45 @@ function _traverseIncludes(includes, rootModel, filter, resultQuery) {
|
|
|
355
381
|
// Build query for this include.
|
|
356
382
|
const associationQuery = traverse(include, filter.includes[includeName], includeModel);
|
|
357
383
|
|
|
358
|
-
|
|
384
|
+
addAssociationQuery(associationQuery, includeName, resultQuery);
|
|
359
385
|
}
|
|
360
386
|
}
|
|
361
387
|
|
|
388
|
+
function _traverseIncludedOrders(resultQuery, rootModel) {
|
|
389
|
+
for (let i=0; i < resultQuery.include.length; i++) {
|
|
390
|
+
const include = resultQuery.include[i];
|
|
362
391
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
resultQuery.include.push({
|
|
367
|
-
association: includeName,
|
|
368
|
-
...associationQuery
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
function _parseWhereEntry(attribute, value, whereHolder) {
|
|
374
|
-
let _value = value;
|
|
375
|
-
|
|
376
|
-
// If attribute is Op (not, like, or, etc.):
|
|
377
|
-
if (attribute in Op) {
|
|
378
|
-
// Parse value:
|
|
379
|
-
_value = _parseValue(_value, attribute);
|
|
380
|
-
|
|
381
|
-
const op = Op[attribute];
|
|
382
|
-
whereHolder[op] = _value;
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
whereHolder[attribute] = _parseValue(_value, attribute);
|
|
387
|
-
}
|
|
392
|
+
if (!include?.order) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
388
395
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
attributes,
|
|
393
|
-
functions,
|
|
394
|
-
where,
|
|
395
|
-
includes,
|
|
396
|
-
...clauses
|
|
397
|
-
} = queryNode;
|
|
398
|
-
|
|
399
|
-
return {
|
|
400
|
-
attributes: attributes ?? [],
|
|
401
|
-
clauses: clauses ?? [],
|
|
402
|
-
functions: functions ?? [],
|
|
403
|
-
where: where ?? {},
|
|
404
|
-
includes: includes ?? [],
|
|
405
|
-
};
|
|
406
|
-
}
|
|
396
|
+
if (!resultQuery.order) {
|
|
397
|
+
resultQuery.order = [];
|
|
398
|
+
}
|
|
407
399
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
400
|
+
const { association } = include;
|
|
401
|
+
const {
|
|
402
|
+
associatedModel,
|
|
403
|
+
associationType
|
|
404
|
+
} = getModelAssociationProps(rootModel.associations[association]);
|
|
405
|
+
|
|
406
|
+
switch(associationType) {
|
|
407
|
+
case 'HasMany': {
|
|
408
|
+
resultQuery.order.push([
|
|
409
|
+
{ association },
|
|
410
|
+
...include.order[0]
|
|
411
|
+
]);
|
|
412
|
+
delete resultQuery.include[i].order;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
default:
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
412
418
|
|
|
413
|
-
|
|
414
|
-
return { [op]: rawValue };
|
|
419
|
+
_traverseIncludedOrders(resultQuery.include[i], associatedModel);
|
|
415
420
|
}
|
|
416
421
|
|
|
417
|
-
return
|
|
422
|
+
return resultQuery;
|
|
418
423
|
}
|
|
419
424
|
|
|
420
425
|
function _setValueWithBounds(value, type, bounds) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
parseValue: _parseValue,
|
|
11
|
+
parseWhereEntry: _parseWhereEntry,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function _parseValue(value, attribute) {
|
|
15
|
+
// If value is Object:
|
|
16
|
+
if (typeof value === 'object' && Array.isArray(value) === false) {
|
|
17
|
+
const [ opKey, rawValue ] = (Object.entries(value))[0];
|
|
18
|
+
|
|
19
|
+
const op = Op[opKey];
|
|
20
|
+
return { [op]: rawValue };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _parseWhereEntry(attribute, value, whereHolder) {
|
|
27
|
+
let _value = value;
|
|
28
|
+
|
|
29
|
+
// If attribute is Op (not, like, or, etc.):
|
|
30
|
+
if (attribute in Op) {
|
|
31
|
+
// Parse value:
|
|
32
|
+
_value = _parseValue(_value, attribute);
|
|
33
|
+
|
|
34
|
+
const op = Op[attribute];
|
|
35
|
+
whereHolder[op] = _value;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
whereHolder[attribute] = _parseValue(_value, attribute);
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
disassembleQueryNode: _disassembleQueryNode,
|
|
11
|
+
addAssociationQuery: _addAssociationQuery,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function _disassembleQueryNode(queryNode) {
|
|
15
|
+
// Disassemble current query node:
|
|
16
|
+
const {
|
|
17
|
+
attributes,
|
|
18
|
+
functions,
|
|
19
|
+
where,
|
|
20
|
+
includes,
|
|
21
|
+
...clauses
|
|
22
|
+
} = queryNode;
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
attributes: attributes ?? [],
|
|
26
|
+
clauses: clauses ?? [],
|
|
27
|
+
functions: functions ?? [],
|
|
28
|
+
where: where ?? {},
|
|
29
|
+
includes: includes ?? [],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function _addAssociationQuery(associationQuery, includeName, resultQuery) {
|
|
34
|
+
|
|
35
|
+
// Add all association info into query.
|
|
36
|
+
resultQuery.include.push({
|
|
37
|
+
association: includeName,
|
|
38
|
+
...associationQuery
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
1
8
|
|
|
2
9
|
module.exports = {
|
|
3
10
|
modelHasAssociations: _modelHasAssociations,
|
|
@@ -10,12 +17,16 @@ function _modelHasAssociations(modelDifinition) {
|
|
|
10
17
|
}
|
|
11
18
|
|
|
12
19
|
function _getModelAssociationProps(
|
|
13
|
-
associationDefinition
|
|
14
|
-
requestData
|
|
20
|
+
associationDefinition
|
|
15
21
|
) {
|
|
16
22
|
// Extract neccessary variables and functions:
|
|
17
23
|
const associatedModel = associationDefinition.target;
|
|
18
|
-
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
foreignKey,
|
|
27
|
+
sourceKey
|
|
28
|
+
} = associationDefinition;
|
|
29
|
+
|
|
19
30
|
const {
|
|
20
31
|
associationType,
|
|
21
32
|
accessors,
|
|
@@ -23,8 +34,11 @@ function _getModelAssociationProps(
|
|
|
23
34
|
|
|
24
35
|
return {
|
|
25
36
|
associatedModel,
|
|
26
|
-
foreignKey,
|
|
27
37
|
associationType,
|
|
38
|
+
|
|
39
|
+
foreignKey,
|
|
40
|
+
sourceKey,
|
|
41
|
+
|
|
28
42
|
accessors,
|
|
29
43
|
};
|
|
30
44
|
}
|
|
@@ -7,8 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
module.exports = {
|
|
10
|
+
UUID: _UUID,
|
|
11
|
+
|
|
10
12
|
CHAR: _STRING,
|
|
11
13
|
VARCHAR: _STRING,
|
|
14
|
+
STRING: _STRING,
|
|
15
|
+
TEXT: _TEXT,
|
|
16
|
+
|
|
17
|
+
ENUM: _ENUM,
|
|
12
18
|
|
|
13
19
|
NUMBER: _NUMBER,
|
|
14
20
|
|
|
@@ -17,8 +23,6 @@ module.exports = {
|
|
|
17
23
|
|
|
18
24
|
BOOLEAN: _BOOLEAN,
|
|
19
25
|
|
|
20
|
-
STRING: _STRING,
|
|
21
|
-
TEXT: _TEXT,
|
|
22
26
|
|
|
23
27
|
DATE: _DATE,
|
|
24
28
|
DATETIME: _DATE,
|
|
@@ -26,6 +30,52 @@ module.exports = {
|
|
|
26
30
|
JSON: _JSON
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
function _UUID(value=undefined, options={ fallback:undefined }) {
|
|
34
|
+
try {
|
|
35
|
+
if (typeof value !== 'string')
|
|
36
|
+
throw new Error(`Not a valid UUID`);
|
|
37
|
+
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
catch(ex) {
|
|
41
|
+
return options?.fallback;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function _STRING(value=undefined, options={ fallback:undefined }) {
|
|
47
|
+
try {
|
|
48
|
+
if (typeof value !== 'string')
|
|
49
|
+
throw new Error(`Not a String`);
|
|
50
|
+
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
catch(ex) {
|
|
54
|
+
return options?.fallback;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function _TEXT(value=undefined, options={ fallback:undefined }) {
|
|
59
|
+
try {
|
|
60
|
+
if (typeof value !== 'string')
|
|
61
|
+
throw new Error(`Not a String`);
|
|
62
|
+
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
catch(ex) {
|
|
66
|
+
return options?.fallback;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
function _ENUM(value=undefined, options={ fallback:undefined }) {
|
|
72
|
+
if (value === undefined)
|
|
73
|
+
return options.fallback;
|
|
74
|
+
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
29
79
|
function _isNumber(value) {
|
|
30
80
|
return !isNaN(`${value}`);
|
|
31
81
|
}
|
|
@@ -70,29 +120,6 @@ function _BOOLEAN(value=undefined, options={ fallback:undefined }) {
|
|
|
70
120
|
}
|
|
71
121
|
}
|
|
72
122
|
|
|
73
|
-
function _STRING(value=undefined, options={ fallback:undefined }) {
|
|
74
|
-
try {
|
|
75
|
-
if (typeof value !== 'string')
|
|
76
|
-
throw new Error(`Not a String`);
|
|
77
|
-
|
|
78
|
-
return value;
|
|
79
|
-
}
|
|
80
|
-
catch(ex) {
|
|
81
|
-
return options?.fallback;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function _TEXT(value=undefined, options={ fallback:undefined }) {
|
|
86
|
-
try {
|
|
87
|
-
if (typeof value !== 'string')
|
|
88
|
-
throw new Error(`Not a String`);
|
|
89
|
-
|
|
90
|
-
return value;
|
|
91
|
-
}
|
|
92
|
-
catch(ex) {
|
|
93
|
-
return options?.fallback;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
123
|
|
|
97
124
|
function _DATE(value=undefined, options={ fallback:undefined }) {
|
|
98
125
|
try {
|
|
@@ -123,6 +150,7 @@ function _DATE(value=undefined, options={ fallback:undefined }) {
|
|
|
123
150
|
}
|
|
124
151
|
}
|
|
125
152
|
|
|
153
|
+
|
|
126
154
|
function _JSON(value=undefined, options={ fallback:undefined }) {
|
|
127
155
|
try {
|
|
128
156
|
if (typeof value === 'string')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodester",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "A versatile REST framework for Node.js",
|
|
5
5
|
"directories": {
|
|
6
6
|
"docs": "docs",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"./params": "./lib/structures/Params.js",
|
|
58
58
|
|
|
59
59
|
"./ql/sequelize": "./lib/middlewares/ql/sequelize/index.js",
|
|
60
|
-
"./query/traverse": "./lib/query/traverse.js",
|
|
60
|
+
"./query/traverse": "./lib/query/traverse/index.js",
|
|
61
61
|
|
|
62
62
|
"./route": "./lib/router/route.js",
|
|
63
63
|
"./router": "./lib/router/index.js",
|