nodester 0.0.9 → 0.1.4
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 +16 -2
- package/lib/application/index.js +29 -8
- package/lib/constants/ErrorCodes.js +19 -0
- package/lib/constants/Operations.js +1 -1
- package/lib/controllers/methods/index.js +34 -10
- package/lib/controllers/mixins/index.js +72 -24
- package/lib/database/connection.js +34 -0
- package/lib/database/migration.js +42 -0
- package/lib/database/utils.js +19 -0
- package/lib/facades/methods/index.js +180 -0
- package/lib/facades/mixins/index.js +111 -0
- package/lib/factories/errors/CustomError.js +7 -0
- package/lib/factories/errors/NodesterQueryError.js +23 -0
- package/lib/factories/errors/index.js +10 -3
- package/lib/loggers/dev.js +28 -0
- package/lib/middlewares/formidable/index.js +37 -0
- package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +26 -3
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +67 -15
- package/lib/models/define.js +49 -1
- package/lib/models/mixins.js +76 -67
- package/lib/params/Params.js +10 -7
- package/lib/queries/Colander.js +107 -0
- package/lib/queries/NodesterQueryParams.js +6 -0
- package/lib/queries/traverse.js +381 -0
- package/lib/router/handlers.util.js +22 -2
- package/lib/router/index.js +97 -76
- package/lib/router/markers.js +78 -0
- package/lib/router/route.js +4 -4
- package/lib/router/routes.util.js +35 -5
- package/lib/router/utils.js +30 -0
- package/lib/stacks/MarkersStack.js +1 -1
- package/lib/stacks/MiddlewareStack.js +1 -1
- package/lib/utils/models.js +14 -0
- package/package.json +36 -7
- package/tests/nql.test.js +3 -3
- package/lib/_/n_controllers/Controller.js +0 -474
- package/lib/_/n_controllers/JWTController.js +0 -240
- package/lib/_/n_controllers/ServiceController.js +0 -109
- package/lib/_/n_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/models/Extractor.js +0 -320
- package/lib/preprocessors/IncludesPreprocessor.js +0 -55
- package/lib/preprocessors/QueryPreprocessor.js +0 -64
- package/lib/utils/forms.util.js +0 -22
- /package/lib/{logger → loggers}/console.js +0 -0
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
// Access to database.
|
|
2
|
-
const Sequelize = require('sequelize');
|
|
3
|
-
// JWT service.
|
|
4
|
-
const JWT = require('nodester/services/jwt.service');
|
|
5
|
-
// Utils:
|
|
6
|
-
const { lowerCaseFirstLetter } = require('nodester/utils/strings.util');
|
|
7
|
-
const { addSeconds } = require('nodester/utils/dates.util');
|
|
8
|
-
const Params = require('nodester/facades/FacadeParams');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
module.exports = class JWTFacade {
|
|
12
|
-
constructor(
|
|
13
|
-
disabledRefreshToken,
|
|
14
|
-
roleModels=[],
|
|
15
|
-
accessTokenConfigs=null,
|
|
16
|
-
refreshTokenConfigs=null
|
|
17
|
-
) {
|
|
18
|
-
if (!disabledRefreshToken)
|
|
19
|
-
throw new Erroror('"disabledRefreshToken" argument is invalid.');
|
|
20
|
-
|
|
21
|
-
this.disabledRefreshToken = disabledRefreshToken;
|
|
22
|
-
|
|
23
|
-
this.roleModels = {};
|
|
24
|
-
roleModels.forEach(model => {
|
|
25
|
-
const name = lowerCaseFirstLetter(model?.options?.name?.singular);
|
|
26
|
-
this.roleModels[name] = model;
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
this.service = new JWT(accessTokenConfigs, refreshTokenConfigs);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async login(params) {
|
|
33
|
-
try {
|
|
34
|
-
const {
|
|
35
|
-
email,
|
|
36
|
-
password,
|
|
37
|
-
role
|
|
38
|
-
} = Params(params, {
|
|
39
|
-
email: null,
|
|
40
|
-
password: null,
|
|
41
|
-
role: null
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (Object.keys(this.roleModels).indexOf(role) === -1)
|
|
45
|
-
throw new Error();
|
|
46
|
-
|
|
47
|
-
// Extract model difinition of this type.
|
|
48
|
-
const modelDefinition = this.roleModels[role];
|
|
49
|
-
const instance = await modelDefinition?.findOneByEmail(email);
|
|
50
|
-
|
|
51
|
-
if (!instance)
|
|
52
|
-
throw new Error();
|
|
53
|
-
|
|
54
|
-
const compareResult = await instance?.comparePasswords(password);
|
|
55
|
-
|
|
56
|
-
if (compareResult === false)
|
|
57
|
-
throw new Error();
|
|
58
|
-
|
|
59
|
-
const result = {
|
|
60
|
-
role: role,
|
|
61
|
-
[role]: instance.toJSON(),
|
|
62
|
-
tokens: await this.issueTokens({ modelInstance: instance, modelName: role })
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Send output.
|
|
66
|
-
return Promise.resolve(result);
|
|
67
|
-
}
|
|
68
|
-
catch(error) {
|
|
69
|
-
console.error(error);
|
|
70
|
-
|
|
71
|
-
// Unauthorized on any error:
|
|
72
|
-
const err = new Error();
|
|
73
|
-
err.name = 'Unauthorized';
|
|
74
|
-
return Promise.reject(err);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async issueAccessToken(params) {
|
|
79
|
-
try {
|
|
80
|
-
const {
|
|
81
|
-
parsedRefreshToken,
|
|
82
|
-
modelInstance,
|
|
83
|
-
modelName
|
|
84
|
-
} = Params(params, {
|
|
85
|
-
parsedRefreshToken: null,
|
|
86
|
-
modelInstance: null,
|
|
87
|
-
modelName: null,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
let newAccessToken = null;
|
|
91
|
-
|
|
92
|
-
if (!modelName && !parsedRefreshToken.role) {
|
|
93
|
-
const err = new Error('No "modelName" provided for JWT issue.');
|
|
94
|
-
err.name = "ValidationError";
|
|
95
|
-
err.status = 403;
|
|
96
|
-
throw err;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// If parsed refresh token was provided:
|
|
100
|
-
if (!!parsedRefreshToken) {
|
|
101
|
-
const payload = {
|
|
102
|
-
id: parsedRefreshToken?.id,
|
|
103
|
-
role: parsedRefreshToken.role,
|
|
104
|
-
};
|
|
105
|
-
newAccessToken = await this.service.issueAccessToken(payload);
|
|
106
|
-
}
|
|
107
|
-
// If modelInstance was provided:
|
|
108
|
-
else if (!!modelInstance) {
|
|
109
|
-
const payload = {
|
|
110
|
-
id: modelInstance?.id,
|
|
111
|
-
role: modelName
|
|
112
|
-
};
|
|
113
|
-
newAccessToken = await this.service.issueAccessToken(payload);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
const err = new Error('No "modelInstance" or "parsedRefreshToken" provided for JWT issue.');
|
|
117
|
-
err.name = "ValidationError";
|
|
118
|
-
err.status = 403;
|
|
119
|
-
throw err;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Check if issue was successful.
|
|
123
|
-
if (!newAccessToken) {
|
|
124
|
-
const err = new Error("Could not issue new access token.");
|
|
125
|
-
err.status = 401;
|
|
126
|
-
throw err;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Send output.
|
|
130
|
-
return Promise.resolve(newAccessToken);
|
|
131
|
-
}
|
|
132
|
-
catch(error) {
|
|
133
|
-
return Promise.reject(error);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async issueTokens(params) {
|
|
138
|
-
try {
|
|
139
|
-
const {
|
|
140
|
-
modelInstance,
|
|
141
|
-
modelName,
|
|
142
|
-
noAccessToken,
|
|
143
|
-
noRefreshToken
|
|
144
|
-
} = Params(params, {
|
|
145
|
-
modelInstance: null,
|
|
146
|
-
modelName: null,
|
|
147
|
-
noAccessToken: false,
|
|
148
|
-
noRefreshToken: false
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Prepare payload container.
|
|
152
|
-
let payload = {};
|
|
153
|
-
|
|
154
|
-
if (!!modelInstance && !!modelName) {
|
|
155
|
-
payload = {
|
|
156
|
-
id: modelInstance?.id,
|
|
157
|
-
role: modelName
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
const err = new Error('No "modelInstance" and "modelName" provided for JWT issue.');
|
|
162
|
-
err.name = "ValidationError";
|
|
163
|
-
err.status = 403;
|
|
164
|
-
throw err;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const accessToken = await this.service.issueAccessToken(payload);
|
|
168
|
-
const refreshToken = await this.service.issueRefreshToken(payload);
|
|
169
|
-
|
|
170
|
-
// Prepare output,
|
|
171
|
-
const tokens = {
|
|
172
|
-
accessToken,
|
|
173
|
-
refreshToken
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
if (noAccessToken)
|
|
177
|
-
delete tokens.accessToken;
|
|
178
|
-
|
|
179
|
-
if (noRefreshToken)
|
|
180
|
-
delete tokens.refreshToken;
|
|
181
|
-
|
|
182
|
-
// Send output.
|
|
183
|
-
return Promise.resolve(tokens);
|
|
184
|
-
}
|
|
185
|
-
catch(error) {
|
|
186
|
-
return Promise.reject(error);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async refreshAccessToken(params) {
|
|
191
|
-
try {
|
|
192
|
-
const {
|
|
193
|
-
refreshToken,
|
|
194
|
-
parsedRefreshToken
|
|
195
|
-
} = Params(params, {
|
|
196
|
-
refreshToken: null,
|
|
197
|
-
parsedRefreshToken: null
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// Check if token is not blocked:
|
|
201
|
-
const isActive = await this.isRefreshTokenActive({ refreshToken });
|
|
202
|
-
if (!isActive) {
|
|
203
|
-
const err = new Error('Invalid Token!');
|
|
204
|
-
err.name = 'InvalidToken';
|
|
205
|
-
throw err;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Issue new access token, based on refresh token:
|
|
209
|
-
const accessToken = await this.issueAccessToken({ parsedRefreshToken });
|
|
210
|
-
|
|
211
|
-
// Send output.
|
|
212
|
-
return Promise.resolve(accessToken);
|
|
213
|
-
}
|
|
214
|
-
catch(error) {
|
|
215
|
-
return Promise.reject(error);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async isRefreshTokenActive(params) {
|
|
220
|
-
try {
|
|
221
|
-
const {
|
|
222
|
-
refreshToken
|
|
223
|
-
} = Params(params, {
|
|
224
|
-
refreshToken: null
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
const foundTokens = await this.disabledRefreshToken.selectAll({ token: refreshToken });
|
|
228
|
-
|
|
229
|
-
// Prepare output. Check if provided token was not disabled.
|
|
230
|
-
const isActive = foundTokens.length === 0;
|
|
231
|
-
|
|
232
|
-
// Send output.
|
|
233
|
-
return Promise.resolve(isActive);
|
|
234
|
-
}
|
|
235
|
-
catch(error) {
|
|
236
|
-
return Promise.reject(error);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
async disableRefreshToken(params) {
|
|
241
|
-
try {
|
|
242
|
-
const {
|
|
243
|
-
refreshToken,
|
|
244
|
-
parsedToken
|
|
245
|
-
} = Params(params, {
|
|
246
|
-
refreshToken: null,
|
|
247
|
-
parsedToken: null
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// Unwrap nessessary data.
|
|
251
|
-
const { id, role } = parsedToken;
|
|
252
|
-
|
|
253
|
-
// Find or create:
|
|
254
|
-
const cofParams = {
|
|
255
|
-
[`${ role }_id`]: id,
|
|
256
|
-
token: refreshToken
|
|
257
|
-
};
|
|
258
|
-
const [ disabledRefreshToken, created ] = await this.disabledRefreshToken.createOrFind(cofParams);
|
|
259
|
-
|
|
260
|
-
// Check result,
|
|
261
|
-
const createdStatus = created === true || !!disabledRefreshToken;
|
|
262
|
-
|
|
263
|
-
// Send output.
|
|
264
|
-
const result = {
|
|
265
|
-
status: createdStatus
|
|
266
|
-
}
|
|
267
|
-
return Promise.resolve(result);
|
|
268
|
-
}
|
|
269
|
-
catch(error) {
|
|
270
|
-
return Promise.reject(error);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
package/lib/models/Extractor.js
DELETED
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ModelDataExtractor (/models/Extractor.js)
|
|
3
|
-
* Reads data from Express's request and filters it,
|
|
4
|
-
* based on model parameters and always-present query parameters (limit, skip, etc.).
|
|
5
|
-
*/
|
|
6
|
-
// Constants:
|
|
7
|
-
const { DataTypes } = require('sequelize');
|
|
8
|
-
const IGNORED_KEYS = [
|
|
9
|
-
// Sequilize keys:
|
|
10
|
-
'createdAt',
|
|
11
|
-
'updatedAt',
|
|
12
|
-
'deletedAt',
|
|
13
|
-
|
|
14
|
-
'created_at',
|
|
15
|
-
'updated_at',
|
|
16
|
-
'deleted_at',
|
|
17
|
-
// Procedural keys.
|
|
18
|
-
'should_delete',
|
|
19
|
-
];
|
|
20
|
-
const PROCEDURAL_KEYS = {
|
|
21
|
-
'should_delete': new DataTypes.BOOLEAN()
|
|
22
|
-
};
|
|
23
|
-
// Utils:
|
|
24
|
-
const sntz = require('nodester/utils/sanitizations.util');
|
|
25
|
-
const Params = require('nodester/facades/FacadeParams');
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
module.exports = class ModelDataExtractor {
|
|
29
|
-
|
|
30
|
-
constructor(
|
|
31
|
-
modelDefinition,
|
|
32
|
-
options,
|
|
33
|
-
) {
|
|
34
|
-
if (!modelDefinition) {
|
|
35
|
-
throw new Error('"modelDefinition" attribute is invalid.');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const {
|
|
39
|
-
withFiles
|
|
40
|
-
} = Params(options, {
|
|
41
|
-
withFiles: false
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
this.model = modelDefinition;
|
|
45
|
-
this.attributes = modelDefinition.tableAttributes;
|
|
46
|
-
this.options = {
|
|
47
|
-
withFiles: !!withFiles,
|
|
48
|
-
fileInstanceName: 'file',
|
|
49
|
-
filesArrayName: 'files',
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
extractData(
|
|
54
|
-
modelDefinition,
|
|
55
|
-
parentModelDefinition=null,
|
|
56
|
-
holder={},
|
|
57
|
-
includes=[],
|
|
58
|
-
skipIdValidation=false,
|
|
59
|
-
skipValidation=false,
|
|
60
|
-
) {
|
|
61
|
-
if (!modelDefinition) {
|
|
62
|
-
const err = new Error('"modelDefinition" attribute is invalid.');
|
|
63
|
-
err.name = 'InternalValidationError';
|
|
64
|
-
throw err;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const data = {};
|
|
68
|
-
const errors = {};
|
|
69
|
-
|
|
70
|
-
const attributes = modelDefinition.tableAttributes;
|
|
71
|
-
|
|
72
|
-
// Table attributes minus "special" keys.
|
|
73
|
-
const keysToCheck = Object.keys(attributes)
|
|
74
|
-
.filter(key => !IGNORED_KEYS.includes(key));
|
|
75
|
-
|
|
76
|
-
keysToCheck.forEach((key) => {
|
|
77
|
-
const {
|
|
78
|
-
type,
|
|
79
|
-
allowNull,
|
|
80
|
-
defaultValue,
|
|
81
|
-
primaryKey,
|
|
82
|
-
references
|
|
83
|
-
} = attributes[key];
|
|
84
|
-
|
|
85
|
-
// Extract raw value from passed object.
|
|
86
|
-
const dataValue = holder[key];
|
|
87
|
-
|
|
88
|
-
if (!!primaryKey && !!skipIdValidation) {
|
|
89
|
-
// Just put id to data object:
|
|
90
|
-
if (!!dataValue)
|
|
91
|
-
data[key] = dataValue;
|
|
92
|
-
|
|
93
|
-
// Skip further validation.
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// If this field references some other model:
|
|
98
|
-
if (!!references) {
|
|
99
|
-
const modelReferenced = references.model;
|
|
100
|
-
|
|
101
|
-
// If referenced model is the same as parentModel,
|
|
102
|
-
// skip (Sequilize will handle it):
|
|
103
|
-
if (modelReferenced === parentModelDefinition?.tableName) {
|
|
104
|
-
// Skip further validation.
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// If value is undefined, and null is allowed skip:
|
|
110
|
-
if (dataValue === undefined && allowNull) {
|
|
111
|
-
// Skip further validation.
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// If value is undefined, and null is allowed,
|
|
116
|
-
// set and skip:
|
|
117
|
-
if (dataValue === null && allowNull) {
|
|
118
|
-
data[key] = null;
|
|
119
|
-
// Skip further validation.
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (allowNull === false && (dataValue === undefined || dataValue === null)) {
|
|
124
|
-
|
|
125
|
-
// If default value can be set,
|
|
126
|
-
// or we're allowed to skip validation,
|
|
127
|
-
// skip:
|
|
128
|
-
if (defaultValue !== undefined || (!primaryKey && skipValidation === true)) {
|
|
129
|
-
// Skip further validation.
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return errors[key] = { message: `Field "${key}" can not be null.` };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
data[key] = _sanitizeValue(dataValue, type);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Check procedural keys:
|
|
140
|
-
for (const [ key, dataType ] of Object.entries(PROCEDURAL_KEYS)) {
|
|
141
|
-
// Extract raw value from passed object.
|
|
142
|
-
const dataValue = holder[key];
|
|
143
|
-
|
|
144
|
-
// If value is defined:
|
|
145
|
-
if (dataValue !== undefined) {
|
|
146
|
-
data[key] = _sanitizeValue(dataValue, dataType);
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// If model has associations:
|
|
151
|
-
if (Object.keys(modelDefinition?.associations)?.length > 0) {
|
|
152
|
-
|
|
153
|
-
// Go through association entries:
|
|
154
|
-
const associationEntries = Object.entries( modelDefinition.associations );
|
|
155
|
-
associationEntries.forEach(([
|
|
156
|
-
associationName,
|
|
157
|
-
associationDefinition
|
|
158
|
-
]) => {
|
|
159
|
-
|
|
160
|
-
// If data of this association is present:
|
|
161
|
-
if (!!holder[associationName]) {
|
|
162
|
-
|
|
163
|
-
const associatedModel = associationDefinition.target;
|
|
164
|
-
|
|
165
|
-
const isSingleInstance = Array.isArray( holder[associationName] ) === false;
|
|
166
|
-
|
|
167
|
-
// If single instance of associated model:
|
|
168
|
-
if (isSingleInstance) {
|
|
169
|
-
data[associationName] = this.extractData(
|
|
170
|
-
associatedModel,
|
|
171
|
-
modelDefinition,
|
|
172
|
-
holder[associationName],
|
|
173
|
-
[], // Includes
|
|
174
|
-
skipIdValidation,
|
|
175
|
-
skipValidation
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
// If multiple instances of associated model:
|
|
179
|
-
else {
|
|
180
|
-
data[associationName] = holder[associationName].map((associationData) => {
|
|
181
|
-
return this.extractData(
|
|
182
|
-
associatedModel,
|
|
183
|
-
modelDefinition,
|
|
184
|
-
associationData,
|
|
185
|
-
[], // Includes
|
|
186
|
-
skipIdValidation,
|
|
187
|
-
skipValidation
|
|
188
|
-
);
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// If options for "withFiles" is true,
|
|
196
|
-
// also check files in object.
|
|
197
|
-
if (this.options.withFiles === true) {
|
|
198
|
-
// Check for single file & array of files.
|
|
199
|
-
const keysToCheck = [
|
|
200
|
-
this.options.fileInstanceName,
|
|
201
|
-
this.options.filesArrayName,
|
|
202
|
-
];
|
|
203
|
-
|
|
204
|
-
keysToCheck.filter(key => holder[key] !== undefined)
|
|
205
|
-
.forEach(key => data[key] = holder[key]);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// If errors were set, throw ValidationError:
|
|
209
|
-
if (Object.keys(errors).length > 0) {
|
|
210
|
-
const err = new Error('');
|
|
211
|
-
err.name = 'ValidationError';
|
|
212
|
-
err.details = { ...errors };
|
|
213
|
-
throw err;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return data;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
extractInstanceDataFromObject(
|
|
220
|
-
holder={},
|
|
221
|
-
includes,
|
|
222
|
-
options,
|
|
223
|
-
) {
|
|
224
|
-
const {
|
|
225
|
-
skipIdValidation,
|
|
226
|
-
skipValidation
|
|
227
|
-
} = Params(options, {
|
|
228
|
-
skipIdValidation: false,
|
|
229
|
-
skipValidation: false,
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
return this.extractData(
|
|
233
|
-
this.model,
|
|
234
|
-
null,
|
|
235
|
-
holder,
|
|
236
|
-
includes,
|
|
237
|
-
skipIdValidation,
|
|
238
|
-
skipValidation
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/* ! Warning !
|
|
243
|
-
* Not finished method
|
|
244
|
-
* Do not use!
|
|
245
|
-
*/
|
|
246
|
-
extractArrayDataFromObject(
|
|
247
|
-
holder={},
|
|
248
|
-
includes,
|
|
249
|
-
options,
|
|
250
|
-
) {
|
|
251
|
-
const {
|
|
252
|
-
skipIdValidation,
|
|
253
|
-
skipValidation
|
|
254
|
-
} = Params(options, {
|
|
255
|
-
skipIdValidation: false,
|
|
256
|
-
skipValidation: false,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
const { instances } = holder;
|
|
260
|
-
|
|
261
|
-
// All model instances must be in an array by key "instances".
|
|
262
|
-
// If "instances" is not array, throw error:
|
|
263
|
-
if (Array.isArray(instances) === false) {
|
|
264
|
-
const err = new Error('');
|
|
265
|
-
err.name = 'ValidationError';
|
|
266
|
-
err.details = { message: 'Field "instances" must be an array' };
|
|
267
|
-
throw err;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const results = instances.map((instance) =>
|
|
271
|
-
this.extractData(
|
|
272
|
-
this.model,
|
|
273
|
-
null,
|
|
274
|
-
instance,
|
|
275
|
-
skipIdValidation,
|
|
276
|
-
skipValidation
|
|
277
|
-
)
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
return results;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function _sanitizeValue(
|
|
285
|
-
value,
|
|
286
|
-
dataType,
|
|
287
|
-
fallback=null,
|
|
288
|
-
) {
|
|
289
|
-
let result = null;
|
|
290
|
-
|
|
291
|
-
if (dataType instanceof DataTypes.INTEGER) {
|
|
292
|
-
result = sntz.INT(value, { fallback });
|
|
293
|
-
}
|
|
294
|
-
else if (dataType instanceof DataTypes.DECIMAL) {
|
|
295
|
-
result = sntz.NUMBER(value, { fallback });
|
|
296
|
-
}
|
|
297
|
-
else if (dataType instanceof DataTypes.FLOAT) {
|
|
298
|
-
result = sntz.NUMBER(value, { fallback });
|
|
299
|
-
}
|
|
300
|
-
else if (dataType instanceof DataTypes.STRING) {
|
|
301
|
-
result = sntz.STRING(value, { fallback });
|
|
302
|
-
}
|
|
303
|
-
else if (dataType instanceof DataTypes.TEXT) {
|
|
304
|
-
result = sntz.STRING(value, { fallback });
|
|
305
|
-
}
|
|
306
|
-
else if (dataType instanceof DataTypes.ENUM) {
|
|
307
|
-
result = sntz.STRING(value, { fallback });
|
|
308
|
-
}
|
|
309
|
-
else if (dataType instanceof DataTypes.JSON) {
|
|
310
|
-
result = sntz.JSON(value, { fallback });
|
|
311
|
-
}
|
|
312
|
-
else if (dataType instanceof DataTypes.BOOLEAN) {
|
|
313
|
-
result = sntz.BOOLEAN(value, { fallback });
|
|
314
|
-
}
|
|
315
|
-
else if (dataType instanceof DataTypes.DATE || dataType instanceof DataTypes.DATEONLY) {
|
|
316
|
-
result = sntz.DATE(value, { fallback });
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// Constants:
|
|
2
|
-
const VISITOR = 'visitor';
|
|
3
|
-
const DefaultAvailableParamsForRoles = { [VISITOR]: [] };
|
|
4
|
-
const DefaultStaticParamsForRoles = { [VISITOR]: [] };
|
|
5
|
-
|
|
6
|
-
// Custom error.
|
|
7
|
-
const { Err } = require('nodester/factories/errors');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
module.exports = class IncludesPreprocessor {
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
availableParamsForRoles,
|
|
14
|
-
staticParamsForRoles,
|
|
15
|
-
customProcessFunction=()=>{}
|
|
16
|
-
) {
|
|
17
|
-
this.availableParamsForRoles = availableParamsForRoles ?? DefaultAvailableParamsForRoles;
|
|
18
|
-
this.staticParamsForRoles = staticParamsForRoles ?? DefaultStaticParamsForRoles;
|
|
19
|
-
|
|
20
|
-
this.customProcessFunction = customProcessFunction ? customProcessFunction : ()=>{};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async extract(
|
|
24
|
-
req,
|
|
25
|
-
role
|
|
26
|
-
) {
|
|
27
|
-
const requestIncludes = req.query?.includes ?? [];
|
|
28
|
-
|
|
29
|
-
if (!requestQuery || typeof requestQuery !== 'object') {
|
|
30
|
-
const err = new Err();
|
|
31
|
-
err.name = 'ValidationError';
|
|
32
|
-
throw err;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Get role or set "visitor"
|
|
36
|
-
const _role = typeof role === 'string' && role.length > 1 ? role : VISITOR;
|
|
37
|
-
|
|
38
|
-
const resultIncludes = [];
|
|
39
|
-
|
|
40
|
-
const params = this.availableParamsForRoles[_role] ?? [];
|
|
41
|
-
const staticValues = this.staticParamsForRoles[_role] ?? [];
|
|
42
|
-
|
|
43
|
-
params.forEach((param) => {
|
|
44
|
-
// If such param is set in query:
|
|
45
|
-
if (requestIncludes.indexOf(param) !== -1) {
|
|
46
|
-
resultIncludes.push(param);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Make further preprocessing using custom defined function.
|
|
51
|
-
await this.customProcessFunction.call(this, req, role, resultIncludes);
|
|
52
|
-
|
|
53
|
-
return resultIncludes;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Constants:
|
|
2
|
-
const VISITOR = 'visitor';
|
|
3
|
-
const DefaultAvailableParamsForRoles = { [VISITOR]: [ 'skip', 'limit', 'order' ] };
|
|
4
|
-
const DefaultStaticParamsForRoles = { [VISITOR]: { limit: 50 } };
|
|
5
|
-
|
|
6
|
-
// Custom error.
|
|
7
|
-
const { Err } = require('nodester/factories/errors');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
module.exports = class QueryPreprocessor {
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
availableParamsForRoles,
|
|
14
|
-
staticParamsForRoles,
|
|
15
|
-
customProcessFunction
|
|
16
|
-
) {
|
|
17
|
-
this.availableParamsForRoles = availableParamsForRoles ?? DefaultAvailableParamsForRoles;
|
|
18
|
-
this.staticParamsForRoles = staticParamsForRoles ?? DefaultStaticParamsForRoles;
|
|
19
|
-
|
|
20
|
-
this.customProcessFunction = customProcessFunction ? customProcessFunction : ()=>{};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async extract(
|
|
24
|
-
req,
|
|
25
|
-
role
|
|
26
|
-
) {
|
|
27
|
-
try {
|
|
28
|
-
const requestQuery = req.query;
|
|
29
|
-
|
|
30
|
-
if (!requestQuery || typeof requestQuery !== 'object') {
|
|
31
|
-
const err = new Err();
|
|
32
|
-
err.name = 'ValidationError';
|
|
33
|
-
throw err;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Get role or set "visitor"
|
|
37
|
-
const _role = typeof role === 'string' && role.length > 1 ? role : [VISITOR];
|
|
38
|
-
|
|
39
|
-
const resultQuery = {};
|
|
40
|
-
|
|
41
|
-
const params = this.availableParamsForRoles[_role] ?? [];
|
|
42
|
-
const staticValues = this.staticParamsForRoles[_role] ?? {};
|
|
43
|
-
|
|
44
|
-
params.forEach((param) => {
|
|
45
|
-
// If such param is set in query:
|
|
46
|
-
if (!!requestQuery[param]) {
|
|
47
|
-
resultQuery[param] = staticValues[param] ?? requestQuery[param];
|
|
48
|
-
}
|
|
49
|
-
// If such param is not set, but we have a "static" for it:
|
|
50
|
-
else if (!requestQuery[param] && !!staticValues[param]) {
|
|
51
|
-
resultQuery[param] = staticValues[param];
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Make further preprocessing using customly defined function.
|
|
56
|
-
await this.customProcessFunction.call(this, req, role, resultQuery);
|
|
57
|
-
|
|
58
|
-
return Promise.resolve(resultQuery);
|
|
59
|
-
}
|
|
60
|
-
catch(error) {
|
|
61
|
-
return Promise.reject(error);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|