nodester 0.0.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/LICENSE +21 -0
- package/Readme.md +125 -0
- package/docs/App.md +13 -0
- package/docs/Queries.md +61 -0
- package/docs/Readme.md +2 -0
- package/docs/Routing.md +34 -0
- package/examples/goal/index.js +23 -0
- package/examples/rest/index.js +25 -0
- package/examples/rest/node_modules/.package-lock.json +40 -0
- package/examples/rest/package-lock.json +72 -0
- package/examples/rest/package.json +14 -0
- package/lib/application/MiddlewareStack.js +125 -0
- package/lib/application/http/request.js +462 -0
- package/lib/application/http/response.js +1107 -0
- package/lib/application/http/utils.js +254 -0
- package/lib/application/index.js +292 -0
- package/lib/constants/ConstantsEnum.js +13 -0
- package/lib/constants/ResponseFormats.js +7 -0
- package/lib/controllers/Controller.js +474 -0
- package/lib/controllers/JWTController.js +240 -0
- package/lib/controllers/ServiceController.js +109 -0
- package/lib/controllers/WebController.js +75 -0
- package/lib/facades/Facade.js +388 -0
- package/lib/facades/FacadeParams.js +11 -0
- package/lib/facades/ServiceFacade.js +17 -0
- package/lib/facades/jwt.facade.js +273 -0
- package/lib/factories/errors/CustomError.js +22 -0
- package/lib/factories/errors/index.js +9 -0
- package/lib/factories/responses/api.js +90 -0
- package/lib/factories/responses/html.js +55 -0
- package/lib/logger/console.js +24 -0
- package/lib/models/DisabledRefreshToken.js +68 -0
- package/lib/models/Extractor.js +320 -0
- package/lib/models/define.js +62 -0
- package/lib/models/mixins.js +369 -0
- package/lib/policies/Role.js +77 -0
- package/lib/policies/RoleExtracting.js +97 -0
- package/lib/preprocessors/BodyPreprocessor.js +61 -0
- package/lib/preprocessors/IncludesPreprocessor.js +55 -0
- package/lib/preprocessors/QueryPreprocessor.js +64 -0
- package/lib/routers/Default/index.js +143 -0
- package/lib/routers/Default/layer.js +50 -0
- package/lib/routers/Main/index.js +10 -0
- package/lib/routers/Roles/index.js +81 -0
- package/lib/services/includes.service.js +79 -0
- package/lib/services/jwt.service.js +147 -0
- package/lib/tools/sql.tool.js +82 -0
- package/lib/utils/dates.util.js +23 -0
- package/lib/utils/forms.util.js +22 -0
- package/lib/utils/json.util.js +49 -0
- package/lib/utils/mappers/Routes/index.js +100 -0
- package/lib/utils/mappers/Routes/utils.js +20 -0
- package/lib/utils/modelAssociations.util.js +44 -0
- package/lib/utils/objects.util.js +69 -0
- package/lib/utils/params.util.js +19 -0
- package/lib/utils/path.util.js +26 -0
- package/lib/utils/queries.util.js +240 -0
- package/lib/utils/sanitizations.util.js +111 -0
- package/lib/utils/sql.util.js +78 -0
- package/lib/utils/strings.util.js +43 -0
- package/lib/utils/types.util.js +26 -0
- package/package.json +63 -0
- package/tests/index.test.js +35 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
exports = module.exports = {
|
|
9
|
+
stringify: _stringify
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Stringify JSON, like JSON.stringify, but v8 optimized, with the
|
|
14
|
+
* ability to escape characters that can trigger HTML sniffing.
|
|
15
|
+
*
|
|
16
|
+
* @param {*} value
|
|
17
|
+
* @param {function} replacer
|
|
18
|
+
* @param {number} spaces
|
|
19
|
+
* @param {boolean} escape
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*
|
|
22
|
+
* @alias stringify
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
function _stringify (value, replacer, spaces, escape) {
|
|
26
|
+
// v8 checks arguments.length for optimizing simple call
|
|
27
|
+
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
|
|
28
|
+
var json = replacer || spaces
|
|
29
|
+
? JSON.stringify(value, replacer, spaces)
|
|
30
|
+
: JSON.stringify(value);
|
|
31
|
+
|
|
32
|
+
if (escape && typeof json === 'string') {
|
|
33
|
+
json = json.replace(/[<>&]/g, function (c) {
|
|
34
|
+
switch (c.charCodeAt(0)) {
|
|
35
|
+
case 0x3c:
|
|
36
|
+
return '\\u003c'
|
|
37
|
+
case 0x3e:
|
|
38
|
+
return '\\u003e'
|
|
39
|
+
case 0x26:
|
|
40
|
+
return '\\u0026'
|
|
41
|
+
/* istanbul ignore next: unreachable default */
|
|
42
|
+
default:
|
|
43
|
+
return c
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return json
|
|
49
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const cwd = process.cwd();
|
|
5
|
+
|
|
6
|
+
// Utils:
|
|
7
|
+
const { isString } = require('util');
|
|
8
|
+
const {
|
|
9
|
+
isConstructor,
|
|
10
|
+
splitByLastDot
|
|
11
|
+
} = require('./utils');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module.exports = function RoutesMapper(
|
|
15
|
+
routes,
|
|
16
|
+
pathToController,
|
|
17
|
+
middlewareGenerals = []
|
|
18
|
+
) {
|
|
19
|
+
const router = express.Router();
|
|
20
|
+
|
|
21
|
+
let requestMethodPath;
|
|
22
|
+
let requestMethod;
|
|
23
|
+
|
|
24
|
+
let controllerMethod;
|
|
25
|
+
let controller;
|
|
26
|
+
let contr;
|
|
27
|
+
|
|
28
|
+
let handler;
|
|
29
|
+
|
|
30
|
+
let myPath;
|
|
31
|
+
const myPathToController = path.join(cwd, pathToController);
|
|
32
|
+
|
|
33
|
+
Object.entries(routes).forEach((value) => {
|
|
34
|
+
let middlewares;
|
|
35
|
+
// To let use an array or only one function as general middlewares:
|
|
36
|
+
if (Array.isArray(middlewareGenerals)) {
|
|
37
|
+
middlewares = [ ...middlewareGenerals ];
|
|
38
|
+
}
|
|
39
|
+
else if (typeof middlewareGenerals === 'function') {
|
|
40
|
+
middlewares = [ middlewareGenerals ];
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
middlewares = [];
|
|
44
|
+
}
|
|
45
|
+
requestMethodPath = value[0].replace(/\s\s+/g, ' ');
|
|
46
|
+
requestMethod = requestMethodPath.split(' ')[0].toLocaleLowerCase();
|
|
47
|
+
myPath = requestMethodPath.split(' ')[1];
|
|
48
|
+
|
|
49
|
+
if (isString(value[1])) {
|
|
50
|
+
controller = splitByLastDot(value[1])[0];
|
|
51
|
+
controllerMethod = splitByLastDot(value[1])[1];
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Contains middlewares and other configuration.
|
|
55
|
+
const props = value[1];
|
|
56
|
+
|
|
57
|
+
// Extract controller paths:
|
|
58
|
+
if (props.path !== undefined) {
|
|
59
|
+
controller = splitByLastDot(props.path)[0];
|
|
60
|
+
controllerMethod = splitByLastDot(props.path)[1];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Extract middlewares:
|
|
64
|
+
if (
|
|
65
|
+
props.middlewares !== undefined &&
|
|
66
|
+
Array.isArray(props.middlewares)
|
|
67
|
+
) {
|
|
68
|
+
middlewares.push(...props.middlewares);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
middlewares = middlewares.filter(el => el != null);
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
handler = require(`${ myPathToController }${ controller }`);
|
|
75
|
+
const isConstructable = isConstructor(handler);
|
|
76
|
+
const type = typeof handler;
|
|
77
|
+
|
|
78
|
+
if (isConstructable) {
|
|
79
|
+
contr = new handler();
|
|
80
|
+
}
|
|
81
|
+
else if (type === 'function') {
|
|
82
|
+
contr = handler();
|
|
83
|
+
}
|
|
84
|
+
else if (type === 'object') {
|
|
85
|
+
contr = handler;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.error('Routes mapper error:', err);
|
|
90
|
+
|
|
91
|
+
require('@babel/register');
|
|
92
|
+
handler = require(`${ myPathToController }${ controller }`).default;
|
|
93
|
+
contr = new handler();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
router.route(myPath)[requestMethod](middlewares, contr[controllerMethod].bind(contr));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return router;
|
|
100
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
isConstructor: _isConstructor,
|
|
4
|
+
splitByLastDot: _splitByLastDot,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function _isConstructor(functionOrClass) {
|
|
8
|
+
try {
|
|
9
|
+
new functionOrClass();
|
|
10
|
+
} catch (err) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function _splitByLastDot(str) {
|
|
18
|
+
const index = str.lastIndexOf('.');
|
|
19
|
+
return [str.slice(0, index), str.slice(index + 1)];
|
|
20
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
modelHasAssociations: _modelHasAssociations,
|
|
4
|
+
getModelAssociationProps: _getModelAssociationProps,
|
|
5
|
+
compileModelAssociationData: _compileModelAssociationData,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function _modelHasAssociations(modelDifinition) {
|
|
9
|
+
return Object.keys(modelDifinition.associations)?.length > 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function _getModelAssociationProps(
|
|
13
|
+
associationDefinition,
|
|
14
|
+
requestData
|
|
15
|
+
) {
|
|
16
|
+
// Extract neccessary variables and functions:
|
|
17
|
+
const associatedModel = associationDefinition.target;
|
|
18
|
+
const { foreignKey } = associationDefinition.options;
|
|
19
|
+
const {
|
|
20
|
+
associationType,
|
|
21
|
+
accessors,
|
|
22
|
+
} = associationDefinition;
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
associatedModel,
|
|
26
|
+
foreignKey,
|
|
27
|
+
associationType,
|
|
28
|
+
accessors,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function _compileModelAssociationData({
|
|
33
|
+
dataOfAssociation,
|
|
34
|
+
parentForeignKey,
|
|
35
|
+
parentModelId,
|
|
36
|
+
}) {
|
|
37
|
+
const result = {
|
|
38
|
+
...dataOfAssociation,
|
|
39
|
+
[parentForeignKey]: parentModelId,
|
|
40
|
+
deletedAt: null
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
copyWithCertainAttributes: _copyWithCertainAttributes,
|
|
10
|
+
merge: _merge
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Copy key-value of target object
|
|
15
|
+
*
|
|
16
|
+
* @param {object} targetObj Object to copy attributes from
|
|
17
|
+
* @param {array} attributes Array of keys
|
|
18
|
+
* @returns {object} New object with attributes of targetObj
|
|
19
|
+
*
|
|
20
|
+
* @alias copyWithCertainAttributes
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
function _copyWithCertainAttributes(targetObj={}, attributes=[]) {
|
|
24
|
+
const result = {};
|
|
25
|
+
|
|
26
|
+
attributes.forEach(a => result[a] = targetObj[a]);
|
|
27
|
+
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Merge the property descriptors of `src` into `dest`
|
|
33
|
+
*
|
|
34
|
+
* @param {object} dest Object to add descriptors to
|
|
35
|
+
* @param {object} src Object to clone descriptors from
|
|
36
|
+
* @param {boolean} [redefine=true] Redefine `dest` properties with `src` properties
|
|
37
|
+
* @returns {object} Reference to dest
|
|
38
|
+
*
|
|
39
|
+
* @alias merge
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
function _merge (dest={}, src={}, redefine=true) {
|
|
43
|
+
if (!dest) {
|
|
44
|
+
throw new TypeError('argument dest is required')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!src) {
|
|
48
|
+
throw new TypeError('argument src is required')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (redefine === true) {
|
|
52
|
+
dest = Object.assign(dest, src);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
Object.getOwnPropertyNames(src)
|
|
56
|
+
.forEach(function forEachOwnPropertyName(name) {
|
|
57
|
+
if (!redefine && hasOwnProperty.call(dest, name)) {
|
|
58
|
+
// Skip descriptor.
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Copy descriptor:
|
|
63
|
+
const descriptor = Object.getOwnPropertyDescriptor(src, name)
|
|
64
|
+
Object.defineProperty(dest, name, descriptor)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return dest;
|
|
69
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = Params;
|
|
3
|
+
|
|
4
|
+
function Params(
|
|
5
|
+
sourceObject={},
|
|
6
|
+
defaultValuesList={}
|
|
7
|
+
) {
|
|
8
|
+
const result = {};
|
|
9
|
+
|
|
10
|
+
const keys = Object.keys(defaultValuesList);
|
|
11
|
+
for (const key of keys) {
|
|
12
|
+
result[key] = typeof sourceObject[key] !== 'boolean' && !sourceObject[key] ?
|
|
13
|
+
defaultValuesList[key]
|
|
14
|
+
:
|
|
15
|
+
sourceObject[key];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
exports = module.exports = {
|
|
3
|
+
isAbsolute: _isAbsolute
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if `path` looks absolute.
|
|
8
|
+
*
|
|
9
|
+
* @param {String} path
|
|
10
|
+
* @return {Boolean}
|
|
11
|
+
* @api private
|
|
12
|
+
* @alias isAbsolute
|
|
13
|
+
*/
|
|
14
|
+
function _isAbsolute(path) {
|
|
15
|
+
// Unix:
|
|
16
|
+
if ('/' === path[0])
|
|
17
|
+
return true;
|
|
18
|
+
|
|
19
|
+
// Windows:
|
|
20
|
+
if (':' === path[1] && ('\\' === path[2] || '/' === path[2]))
|
|
21
|
+
return true;
|
|
22
|
+
|
|
23
|
+
// Microsoft Azure:
|
|
24
|
+
if ('\\\\' === path.substring(0, 2))
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Constants.
|
|
2
|
+
const SubIncludesQueryRegex = /\([^)]*\)/g;
|
|
3
|
+
|
|
4
|
+
// Sequelize.
|
|
5
|
+
const Op = require('sequelize').Op;
|
|
6
|
+
|
|
7
|
+
// Utils.
|
|
8
|
+
const {
|
|
9
|
+
splitByDot,
|
|
10
|
+
splitByAmpersand
|
|
11
|
+
} = require('nodester/utils/strings.util');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
parseQueryParams: _parseQueryParams,
|
|
16
|
+
deleteQuerySortParams: _deleteQuerySortParams,
|
|
17
|
+
|
|
18
|
+
// SubIncludes Query:
|
|
19
|
+
hasSubIncludesQuery: _hasSubIncludesQuery,
|
|
20
|
+
cutSubIncludesQuery: _cutSubIncludesQuery,
|
|
21
|
+
parseSubIncludesQuery: _parseSubIncludesQuery
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function _parseQueryParams(
|
|
25
|
+
requestQueryObject={},
|
|
26
|
+
sequilizeQuery=null
|
|
27
|
+
) {
|
|
28
|
+
const skip = parseInt(requestQueryObject?.skip ?? 0);
|
|
29
|
+
const limit = parseInt(requestQueryObject?.limit ?? 50);
|
|
30
|
+
|
|
31
|
+
sequilizeQuery.offset = skip;
|
|
32
|
+
sequilizeQuery.limit = limit;
|
|
33
|
+
|
|
34
|
+
const order = requestQueryObject?.order;
|
|
35
|
+
|
|
36
|
+
// If order is set:
|
|
37
|
+
if (!!order) {
|
|
38
|
+
const orderBy = requestQueryObject?.order_by ?? 'id';
|
|
39
|
+
|
|
40
|
+
sequilizeQuery.order = [
|
|
41
|
+
[ orderBy, order ]
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Clear sort params.
|
|
46
|
+
_deleteQuerySortParams(requestQueryObject);
|
|
47
|
+
|
|
48
|
+
// Get include names.
|
|
49
|
+
const _includes = sequilizeQuery?.include?.map( include => include.association ) ?? [];
|
|
50
|
+
|
|
51
|
+
// Count query keys.
|
|
52
|
+
const keysCount = Object.keys(requestQueryObject).length;
|
|
53
|
+
|
|
54
|
+
// If query has no keys,
|
|
55
|
+
// stop further execution:
|
|
56
|
+
if (keysCount === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// This container is a reference to current "where".
|
|
61
|
+
let container = null;
|
|
62
|
+
let isContainerArray = false;
|
|
63
|
+
|
|
64
|
+
// If query has only 1 key:
|
|
65
|
+
if (keysCount === 1) {
|
|
66
|
+
// Define empty query's where.
|
|
67
|
+
sequilizeQuery.where = {};
|
|
68
|
+
container = sequilizeQuery.where;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Define conjuction of params in query's where.
|
|
72
|
+
sequilizeQuery.where = {
|
|
73
|
+
[Op.and]: []
|
|
74
|
+
};
|
|
75
|
+
container = sequilizeQuery.where[Op.and];
|
|
76
|
+
isContainerArray = true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Go through query's keys:
|
|
80
|
+
Object.keys(requestQueryObject)
|
|
81
|
+
.forEach((queryKey) => {
|
|
82
|
+
|
|
83
|
+
// Parse value of this key:
|
|
84
|
+
const value = requestQueryObject[queryKey];
|
|
85
|
+
// If value is not a number, parse it further.
|
|
86
|
+
const parsedValue = isNaN( value ) ? _parseHTTPQueryValue( `${ requestQueryObject[queryKey] }` ) : value;
|
|
87
|
+
|
|
88
|
+
// If we got nested key:
|
|
89
|
+
if (queryKey.indexOf('.') !== -1) {
|
|
90
|
+
|
|
91
|
+
// If this key is included as association:
|
|
92
|
+
const associationIndex = _includes.indexOf(queryKey.split('.')[0]);
|
|
93
|
+
if (associationIndex > -1) {
|
|
94
|
+
sequilizeQuery.include[associationIndex].where = { [`$${queryKey}$`]: parsedValue };
|
|
95
|
+
}
|
|
96
|
+
// Use special sequelize syntax for nested SELECT:
|
|
97
|
+
else if (isContainerArray) {
|
|
98
|
+
const selectObject = { [`$${queryKey}$`]: parsedValue };
|
|
99
|
+
container.push(selectObject);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
container[`$${queryKey}$`] = parsedValue;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// On regular key, set regular key-value pair:
|
|
106
|
+
else {
|
|
107
|
+
if (isContainerArray) {
|
|
108
|
+
const selectObject = { [queryKey]: parsedValue };
|
|
109
|
+
container.push(selectObject);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
container[queryKey] = parsedValue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function _parseHTTPQueryValue(
|
|
119
|
+
value='',
|
|
120
|
+
isNumber=false
|
|
121
|
+
) {
|
|
122
|
+
// If value matches "and()":
|
|
123
|
+
if (value.slice(0, 4) === 'and(') {
|
|
124
|
+
// Remove "and()".
|
|
125
|
+
const clearValuesString = value.substr(4, value.length - 'and()'.length);
|
|
126
|
+
|
|
127
|
+
const clearValues = clearValuesString.split(',');
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
[Op.and]: clearValues.map(cv => _parseHTTPQueryValue(cv))
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// If value matches "like(value)":
|
|
134
|
+
else if (value.slice(0, 5) === 'like(') {
|
|
135
|
+
// Remove "like()".
|
|
136
|
+
const clearValue = value.substr(5, value.length - 'like()'.length);
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
[Op.like]: `%${ _parseHTTPQueryValue(clearValue) }%`
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// If value matches "notLike(value)":
|
|
143
|
+
else if (value.slice(0, 8) === 'notLike(') {
|
|
144
|
+
// Remove "notLike()".
|
|
145
|
+
const clearValue = value.substr(8, value.length - 'notLike()'.length);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
[Op.notLike]: `%${ _parseHTTPQueryValue(clearValue) }%`
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// If value matches "not(value)":
|
|
152
|
+
else if (value.slice(0, 4) === 'not(') {
|
|
153
|
+
// Remove "not()".
|
|
154
|
+
const clearValue = value.substr(4, value.length - 'not()'.length);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
[Op.not]: _parseHTTPQueryValue(clearValue)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// If value matches "or()":
|
|
161
|
+
else if (value.slice(0, 3) === 'or(') {
|
|
162
|
+
// Remove "or()".
|
|
163
|
+
const clearValuesString = value.substr(3, value.length - 'or()'.length);
|
|
164
|
+
|
|
165
|
+
const clearValues = clearValuesString.split(',');
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
[Op.or]: clearValues.map(cv => _parseHTTPQueryValue(cv))
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// If value is a number:
|
|
172
|
+
// else if (!isNaN(value)) {
|
|
173
|
+
// const number = parseFloat(value);
|
|
174
|
+
// return number;
|
|
175
|
+
// }
|
|
176
|
+
// For default, just set this value in "where"
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function _deleteQuerySortParams(requestQueryObject={}) {
|
|
181
|
+
delete requestQueryObject.skip;
|
|
182
|
+
delete requestQueryObject.limit;
|
|
183
|
+
delete requestQueryObject.order;
|
|
184
|
+
delete requestQueryObject.order_by;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// If string has nested query "()":
|
|
188
|
+
function _hasSubIncludesQuery(string='') {
|
|
189
|
+
return SubIncludesQueryRegex.test(string);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function _cutSubIncludesQuery(string='') {
|
|
193
|
+
const query = string.match(SubIncludesQueryRegex)[0];
|
|
194
|
+
const newString = string.replace(SubIncludesQueryRegex, '');
|
|
195
|
+
|
|
196
|
+
return [ query, newString ];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function _parseSubIncludesQuery(
|
|
200
|
+
associationName,
|
|
201
|
+
subIncludesQuery='',
|
|
202
|
+
sequilizeQuery=null
|
|
203
|
+
) {
|
|
204
|
+
const query = {};
|
|
205
|
+
|
|
206
|
+
// Magic trick to make everything work (DO NOT REMOVE).
|
|
207
|
+
sequilizeQuery.separate = true;
|
|
208
|
+
|
|
209
|
+
// If first "("" & ")" are set, cut them:
|
|
210
|
+
const subIncludesQueryRegex = /\([^)]*\)/g;
|
|
211
|
+
const clearQuery = subIncludesQueryRegex.test(subIncludesQuery) ?
|
|
212
|
+
subIncludesQuery.substr(1, subIncludesQuery.length - 2)
|
|
213
|
+
:
|
|
214
|
+
subIncludesQuery;
|
|
215
|
+
|
|
216
|
+
const keyValues = splitByAmpersand(clearQuery).map(kv => {
|
|
217
|
+
const keyValue = kv.split('=');
|
|
218
|
+
query[keyValue[0]] = keyValue[1];
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
if (!!query.skip) {
|
|
223
|
+
const skip = parseInt(query.skip ?? 0);
|
|
224
|
+
sequilizeQuery[`$${ associationName }.offset$`] = skip;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!!query.limit) {
|
|
228
|
+
const limit = parseInt(query.limit ?? 50);
|
|
229
|
+
sequilizeQuery[`$${ associationName }.limit$`] = limit;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// If order is set:
|
|
233
|
+
if (!!query.order) {
|
|
234
|
+
const orderBy = query?.order_by ?? 'id';
|
|
235
|
+
|
|
236
|
+
sequilizeQuery.order = [
|
|
237
|
+
[ orderBy, query.order ]
|
|
238
|
+
];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = {
|
|
3
|
+
NUMBER: _NUMBER,
|
|
4
|
+
INT: _INT,
|
|
5
|
+
BOOLEAN: _BOOLEAN,
|
|
6
|
+
STRING: _STRING,
|
|
7
|
+
|
|
8
|
+
DATE: _DATE,
|
|
9
|
+
|
|
10
|
+
JSON: _JSON
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function _isNumber(value) {
|
|
14
|
+
return !isNaN(`${value}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function _NUMBER(value=undefined, options={ fallback:undefined, min:undefined, max:undefined }) {
|
|
18
|
+
try {
|
|
19
|
+
if (!_isNumber(value))
|
|
20
|
+
throw new Error(`Not a number`);
|
|
21
|
+
|
|
22
|
+
if (_isNumber(options?.min) && value < options?.min)
|
|
23
|
+
return options?.min;
|
|
24
|
+
|
|
25
|
+
if (_isNumber(options?.max) && value > options?.max)
|
|
26
|
+
return options?.max;
|
|
27
|
+
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
catch(ex) {
|
|
31
|
+
return options?.fallback;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function _INT(value=undefined, options={ fallback:undefined, min:undefined, max:undefined }) {
|
|
36
|
+
const num = _NUMBER(value, { fallback:undefined, min:options?.min, max:options?.max });
|
|
37
|
+
return num === undefined ? options?.fallback : parseInt(num);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function _BOOLEAN(value=undefined, options={ fallback:undefined }) {
|
|
41
|
+
try {
|
|
42
|
+
// If clear boolean.
|
|
43
|
+
if (value === true || value === false || toString.call(value) === '[object Boolean]')
|
|
44
|
+
return value;
|
|
45
|
+
|
|
46
|
+
// If string-boolean.
|
|
47
|
+
if (typeof value === "string")
|
|
48
|
+
return value === "true";
|
|
49
|
+
|
|
50
|
+
throw new Error(`Not a Boolean`);
|
|
51
|
+
}
|
|
52
|
+
catch(ex) {
|
|
53
|
+
return options?.fallback;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _STRING(value=undefined, options={ fallback:undefined }) {
|
|
58
|
+
try {
|
|
59
|
+
if (typeof value !== "string")
|
|
60
|
+
throw new Error(`Not a String`);
|
|
61
|
+
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
catch(ex) {
|
|
65
|
+
return options?.fallback;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function _DATE(value=undefined, options={ fallback:undefined }) {
|
|
70
|
+
try {
|
|
71
|
+
const type = Object.prototype.toString.call(value);
|
|
72
|
+
|
|
73
|
+
switch(type) {
|
|
74
|
+
case('[object Date]'): {
|
|
75
|
+
if (isNaN(value.valueOf())){
|
|
76
|
+
throw new Error('Not a date');
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
case('[object String]'): {
|
|
81
|
+
const check = new Date(value);
|
|
82
|
+
if (check instanceof Date)
|
|
83
|
+
return value;
|
|
84
|
+
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
default:
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
throw new Error('Not a date');
|
|
92
|
+
}
|
|
93
|
+
catch(ex) {
|
|
94
|
+
return options?.fallback;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function _JSON(value=undefined, options={ fallback:undefined }) {
|
|
99
|
+
try {
|
|
100
|
+
if (typeof value === "string")
|
|
101
|
+
return JSON.parse(value);
|
|
102
|
+
|
|
103
|
+
if (typeof value !== 'object')
|
|
104
|
+
throw new Error(`Not an object`);
|
|
105
|
+
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
catch(ex) {
|
|
109
|
+
return options?.fallback;
|
|
110
|
+
}
|
|
111
|
+
}
|