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.
Files changed (48) hide show
  1. package/Readme.md +16 -2
  2. package/lib/application/index.js +29 -8
  3. package/lib/constants/ErrorCodes.js +19 -0
  4. package/lib/constants/Operations.js +1 -1
  5. package/lib/controllers/methods/index.js +34 -10
  6. package/lib/controllers/mixins/index.js +72 -24
  7. package/lib/database/connection.js +34 -0
  8. package/lib/database/migration.js +42 -0
  9. package/lib/database/utils.js +19 -0
  10. package/lib/facades/methods/index.js +180 -0
  11. package/lib/facades/mixins/index.js +111 -0
  12. package/lib/factories/errors/CustomError.js +7 -0
  13. package/lib/factories/errors/NodesterQueryError.js +23 -0
  14. package/lib/factories/errors/index.js +10 -3
  15. package/lib/loggers/dev.js +28 -0
  16. package/lib/middlewares/formidable/index.js +37 -0
  17. package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +26 -3
  18. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +67 -15
  19. package/lib/models/define.js +49 -1
  20. package/lib/models/mixins.js +76 -67
  21. package/lib/params/Params.js +10 -7
  22. package/lib/queries/Colander.js +107 -0
  23. package/lib/queries/NodesterQueryParams.js +6 -0
  24. package/lib/queries/traverse.js +381 -0
  25. package/lib/router/handlers.util.js +22 -2
  26. package/lib/router/index.js +97 -76
  27. package/lib/router/markers.js +78 -0
  28. package/lib/router/route.js +4 -4
  29. package/lib/router/routes.util.js +35 -5
  30. package/lib/router/utils.js +30 -0
  31. package/lib/stacks/MarkersStack.js +1 -1
  32. package/lib/stacks/MiddlewareStack.js +1 -1
  33. package/lib/utils/models.js +14 -0
  34. package/package.json +36 -7
  35. package/tests/nql.test.js +3 -3
  36. package/lib/_/n_controllers/Controller.js +0 -474
  37. package/lib/_/n_controllers/JWTController.js +0 -240
  38. package/lib/_/n_controllers/ServiceController.js +0 -109
  39. package/lib/_/n_controllers/WebController.js +0 -75
  40. package/lib/_facades/Facade.js +0 -388
  41. package/lib/_facades/FacadeParams.js +0 -11
  42. package/lib/_facades/ServiceFacade.js +0 -17
  43. package/lib/_facades/jwt.facade.js +0 -273
  44. package/lib/models/Extractor.js +0 -320
  45. package/lib/preprocessors/IncludesPreprocessor.js +0 -55
  46. package/lib/preprocessors/QueryPreprocessor.js +0 -64
  47. package/lib/utils/forms.util.js +0 -22
  48. /package/lib/{logger → loggers}/console.js +0 -0
@@ -1,109 +0,0 @@
1
- // Query preprocessor.
2
- const QueryPreprocessor = require('nodester/preprocessors/QueryPreprocessor');
3
- // Reponse protocol generator.
4
- const APIResponseFactory = require('nodester/factories/responses/api');
5
- // Custom error.
6
- const { Err } = require('nodester/factories/errors');
7
-
8
-
9
- module.exports = class ServiceController {
10
- constructor({
11
- name,
12
- queryPreprocessor,
13
- apiResponseFactory,
14
- }) {
15
- if (!name) {
16
- const err = new Err();
17
- err.details = 'Argument "name" is required';
18
- throw err;
19
- }
20
- // Set private name of this controller.
21
- this.name = name;
22
-
23
- // Set preprocessors:
24
- this.queryPreprocessor = queryPreprocessor ?? new QueryPreprocessor();
25
- // TODO: body preprocessor.
26
-
27
- // Init standard API response factory.
28
- const standardAPIResponseFactory = new APIResponseFactory();
29
- // Set response factory:
30
- this.createOKResponse = apiResponseFactory?.createOKResponse ??
31
- standardAPIResponseFactory.createOKResponse.bind(standardAPIResponseFactory);
32
- this.createErrorResponse = apiResponseFactory?.createErrorResponse ??
33
- standardAPIResponseFactory.createErrorResponse.bind(standardAPIResponseFactory);
34
- }
35
-
36
- processError(error, req, res) {
37
- // Default error message.
38
- let errorMessage = error?.message ?? 'Internal server error';
39
- // Default HTTP status code.
40
- let statusCode = error?.status ?? error?.statusCode ?? 500;
41
- // Error response object.
42
- let errorResponse = {};
43
-
44
- switch(error.name) {
45
- case('NotFound'): {
46
- statusCode = 404;
47
- errorResponse.details = { message: errorMessage };
48
- break;
49
- }
50
- case('ValidationError'): {
51
- statusCode = 406;
52
- errorResponse.details = error?.details;
53
- break;
54
- }
55
- case('ConflictError'): {
56
- statusCode = 409;
57
- errorResponse.details = error?.details ?? error?.message;
58
- break;
59
- }
60
- case('SequelizeUniqueConstraintError'): {
61
- statusCode = 409;
62
- errorResponse.details = error?.errors;
63
- break;
64
- }
65
- case('InternalValidationError'): {
66
- statusCode = 500;
67
- errorResponse.details = { message: 'Error' };
68
- break;
69
- }
70
- default: {
71
- errorResponse.details = { message: errorMessage };
72
- break;
73
- }
74
- }
75
-
76
- // Send error response with provided status code.
77
- return this.createErrorResponse({
78
- res,
79
- error: {
80
- ...errorResponse,
81
- code: statusCode
82
- },
83
- status: statusCode
84
- });
85
- }
86
-
87
- // Preprocessors:
88
- async extractQuery(req, res) {
89
- try {
90
- // Extract role.
91
- const role = req?.token?.parsed?.role ?? 'visitor';
92
-
93
- // Extract query:
94
- const query = await this.queryPreprocessor.extract(
95
- req,
96
- role,
97
- );
98
- const { includes } = req.query;
99
-
100
- return Promise.resolve({
101
- query,
102
- includes,
103
- });
104
- }
105
- catch(error) {
106
- return Promise.reject(error);
107
- }
108
- }
109
- }
@@ -1,75 +0,0 @@
1
- // Data extractor:
2
- const Extractor = require('nodester/models/Extractor');
3
- // Query preprocessor.
4
- const QueryPreprocessor = require('nodester/preprocessors/QueryPreprocessor');
5
- // Reponse protocol generator.
6
- const WebResponseFactory = require('nodester/factories/responses/html');
7
- // Custom error.
8
- const { Err } = require('nodester/factories/errors');
9
-
10
-
11
- module.exports = class Controller {
12
- constructor({
13
- modelFacade,
14
- queryPreprocessor,
15
- webResponseFactory,
16
-
17
- // Options.
18
- withFiles,
19
- }) {
20
- if (!modelFacade) {
21
- throw new Error('"modelFacade" argument is invalid.');
22
- }
23
-
24
- // Main model.
25
- const model = modelFacade?.model;
26
-
27
- // Set main services & utils:
28
- this.extractor = new Extractor(model, { withFiles: !!withFiles });
29
- this.facade = modelFacade;
30
-
31
- // Set preprocessors:
32
- // TODO: includes preprocessor.
33
- this.queryPreprocessor = queryPreprocessor ?? new QueryPreprocessor();
34
- // TODO: body preprocessor.
35
-
36
- // Extract plural name of model.
37
- const modelPluralName = model?.options?.name?.plural;
38
- // Set private name of this controller.
39
- this.name = `${modelPluralName ?? '_INVALID_NAME_'}Controller`;
40
-
41
- // Init standard Web response factory.
42
- const standardWebResponseFactory = new WebResponseFactory();
43
- // Set response factory:
44
- this.createOKResponse = webResponseFactory?.createOKResponse ??
45
- standardWebResponseFactory.createOKResponse.bind(standardWebResponseFactory);
46
- this.createErrorResponse = webResponseFactory?.createErrorResponse ??
47
- standardWebResponseFactory.createErrorResponse.bind(standardWebResponseFactory);
48
- }
49
-
50
- // TODO: Implement CRUD.
51
-
52
- // Preprocessors:
53
- async extractQuery(req, res) {
54
- try {
55
- // Extract role.
56
- const role = req?.token?.parsed?.role ?? 'visitor';
57
-
58
- // Extract query:
59
- const query = await this.queryPreprocessor.extract(
60
- req,
61
- role,
62
- );
63
- const { includes } = req.query;
64
-
65
- return Promise.resolve({
66
- query,
67
- includes,
68
- });
69
- }
70
- catch(error) {
71
- return Promise.reject(error);
72
- }
73
- }
74
-
75
- }
@@ -1,388 +0,0 @@
1
- const Params = require('nodester/facades/FacadeParams');
2
- // Utils:
3
- const { lowerCaseFirstLetter } = require('nodester/utils/strings.util');
4
- const { parseIncludesQuery } = require('nodester/services/includes.service');
5
- const { parseQueryParams } = require('nodester/utils/queries.util');
6
-
7
-
8
- module.exports = class Facade {
9
- constructor(modelDefinition) {
10
- if (!modelDefinition)
11
- throw new Error('"modelDefinition" argument is invalid.');
12
-
13
- this.model = modelDefinition;
14
- this.nameSingular = lowerCaseFirstLetter(modelDefinition?.options?.name?.singular);
15
- this.namePlural = lowerCaseFirstLetter(modelDefinition?.options?.name?.plural);
16
- }
17
-
18
- /*
19
- * @param <Object> data
20
- */
21
- async createOne(params) {
22
- try {
23
- const {
24
- data,
25
- includes,
26
- } = Params(params, {
27
- data: null,
28
- includes: null,
29
- });
30
-
31
- // Parse includes string to array.
32
- const _includes = typeof includes === 'string' ? parseIncludesQuery(this.model, includes) : null;
33
-
34
- const instance = await this.model.create({ ...data }, {
35
- include: this.model.getIncludesList(data)
36
- });
37
-
38
- // If includes are set, "find" this record with includes:
39
- if (!!_includes && _includes?.length > 0) {
40
- await instance.reload({ include: _includes });
41
- }
42
-
43
- const result = {
44
- [this.nameSingular]: instance,
45
- count: 1
46
- };
47
-
48
- // Call after create.
49
- await this.afterCreate(instance, params, result);
50
-
51
- // Send output.
52
- return Promise.resolve(result);
53
- }
54
- catch(error) {
55
- return Promise.reject(error);
56
- }
57
- }
58
-
59
- /*
60
- * @param <Object> data
61
- */
62
- async getOrCreateOne(params) {
63
- try {
64
- const {
65
- id,
66
- query,
67
- data,
68
- includes,
69
- } = Params(params, {
70
- id: null,
71
- query: null,
72
- data: null,
73
- includes: null,
74
- });
75
-
76
- // Parse includes string to array.
77
- const _includes = typeof includes === 'string' ? parseIncludesQuery(this.model, includes) : null;
78
-
79
- let instance, isNewRecord;
80
-
81
- if (!!id) {
82
- const [ _instance, _isNewRecord ] = await this.model.findOrCreate({
83
- where: {
84
- id: parseInt(id)
85
- },
86
- defaults: data
87
- });
88
-
89
- // Find this model again with passed includes.
90
- instance = await this.model.findById(id, _includes);
91
-
92
- isNewRecord = _isNewRecord;
93
- }
94
- else if (!!query) {
95
- // New sequilize query.
96
- const modelQuery = {};
97
-
98
- // If includes are set:
99
- if (!!_includes) {
100
- modelQuery.include = [ ..._includes ];
101
- }
102
-
103
- parseQueryParams(query, modelQuery);
104
-
105
- let _instance = await this.model.findOne(modelQuery);
106
-
107
- // Define "isNewRecord".
108
- isNewRecord = !instance;
109
-
110
- // If no record in DB, create new one:
111
- if (!_instance) {
112
- _instance = await this.model.create({ ...data }, {
113
- include: this.model.getIncludesList(data)
114
- });
115
-
116
- instance = _instance;
117
-
118
- // If includes are set, "find" this record with includes:
119
- if (!!_includes && _includes?.length > 0) {
120
- await instance.reload({ include: _includes });
121
- }
122
- }
123
- else {
124
- instance = _instance;
125
- }
126
- }
127
-
128
- const result = {
129
- [this.nameSingular]: instance,
130
- count: 1,
131
- isNewRecord: isNewRecord
132
- };
133
-
134
- // If new record, call "after create":
135
- if (!!isNewRecord) {
136
- await this.afterCreate(instance, params, result);
137
- }
138
-
139
- // Send output.
140
- return Promise.resolve(result);
141
- }
142
- catch(error) {
143
- return Promise.reject(error);
144
- }
145
- }
146
-
147
- /*
148
- * @param <ModelInstance> instance
149
- * @param <Array> params
150
- */
151
- async afterCreate(
152
- instance,
153
- params,
154
- result
155
- ) {
156
- // This method is empty, as it should be overwritten.
157
- return Promise.resolve();
158
- }
159
-
160
- /*
161
- * @param <UInt> id
162
- * @param <Object> query
163
- * @param <Array> includes
164
- */
165
- async getOne(params) {
166
- try {
167
- const {
168
- id,
169
- query,
170
- includes,
171
- } = Params(params, {
172
- id: null,
173
- query: null,
174
- includes: null,
175
- });
176
-
177
- // Parse includes string to array.
178
- const _includes = typeof includes === 'string' ? parseIncludesQuery(this.model, includes) : null;
179
-
180
- let instance = null;
181
-
182
- if (!!id || !!query?.id) {
183
- instance = await this.model.findById(( id ?? query.id ), _includes);
184
- }
185
- else if (!!query) {
186
- // New sequilize query.
187
- const modelQuery = {};
188
-
189
- // If includes are set:
190
- if (!!_includes) {
191
- modelQuery.include = [ ..._includes ];
192
- }
193
-
194
- parseQueryParams(query, modelQuery);
195
-
196
- instance = await this.model.findOne(modelQuery);
197
- }
198
-
199
- const result = {
200
- [this.nameSingular]: instance,
201
- count: !!instance ? 1 : 0
202
- };
203
-
204
- // Send output.
205
- return Promise.resolve(result);
206
- }
207
- catch(error) {
208
- return Promise.reject(error);
209
- }
210
- }
211
-
212
- /*
213
- * @param <Object> query
214
- * @param <Array> includes
215
- */
216
- async getMany(params) {
217
- try {
218
- const {
219
- query,
220
- includes,
221
- } = Params(params, {
222
- query: {},
223
- includes: null
224
- });
225
-
226
- // Parse includes string to array.
227
- const _includes = typeof includes === 'string' ? parseIncludesQuery(this.model, includes) : null;
228
-
229
- // New sequilize query.
230
- const modelQuery = {};
231
-
232
- // If includes are set:
233
- if (!!_includes) {
234
- modelQuery.include = [ ..._includes ];
235
- }
236
-
237
- parseQueryParams(query, modelQuery);
238
-
239
- // Get instances for current limits
240
- // and total number (Int) of such instances.
241
- const [
242
- instances,
243
- totalCount
244
- ] = await Promise.all([
245
- this.model.findAll(modelQuery),
246
- this.model.count(modelQuery)
247
- ]);
248
-
249
- const result = {
250
- [this.namePlural]: instances,
251
-
252
- count: instances.length,
253
- total_count: totalCount,
254
-
255
- limit: modelQuery.limit,
256
- skip: modelQuery.offset
257
- };
258
-
259
- // Send output.
260
- return Promise.resolve(result);
261
- }
262
- catch(error) {
263
- return Promise.reject(error);
264
- }
265
- }
266
-
267
- /*
268
- * @param <UInt> id
269
- * @param <Object> data
270
- * @param <Array> includes
271
- */
272
- async updateOne(params) {
273
- try {
274
- const {
275
- id,
276
- query,
277
- data,
278
- includes,
279
- } = Params(params, {
280
- id: null,
281
- query: {},
282
- data: null,
283
- includes: null,
284
- });
285
-
286
- // Parse includes string to array.
287
- const _includes = typeof includes === 'string' ? parseIncludesQuery(this.model, includes) : null;
288
-
289
- let updateResult = null;
290
-
291
- if (!!id || !!query.id) {
292
- updateResult = await this.model.updateById(( id ?? query.id ), data, _includes);
293
- }
294
- else {
295
- // New sequilize query.
296
- const modelQuery = {};
297
- parseQueryParams(query, modelQuery);
298
-
299
- updateResult = await this.model.updateOne(modelQuery.where, data, _includes);
300
- }
301
-
302
- const [ isNewRecord, instance ] = updateResult;
303
-
304
- const result = {
305
- success: isNewRecord === false,
306
- [this.nameSingular]: instance
307
- };
308
-
309
- // Send output.
310
- return Promise.resolve(result);
311
- }
312
- catch(error) {
313
- return Promise.reject(error);
314
- }
315
- }
316
-
317
- // TODO: finish updateMany:
318
- /*
319
- * @param <UInt> id
320
- * @param <Object> data
321
- * @param <Array> includes
322
- */
323
- // async updateMany(params) {
324
- // try {
325
- // const {
326
- // query,
327
- // data,
328
- // includes,
329
- // } = Params(params, {
330
- // query: {},
331
- // data: null,
332
- // includes: null,
333
- // });
334
-
335
- // const _includes = typeof includes === 'string' ? parseIncludesQuery(this.model, includes) : null;
336
-
337
- // let updateResult = null;
338
-
339
- // if (!!query.id) {
340
- // updateResult = await this.model.updateById(query.id, data, _includes);
341
- // }
342
- // else {
343
- // // New sequilize query.
344
- // const modelQuery = {};
345
- // parseQueryParams(query, modelQuery);
346
-
347
- // updateResult = await this.model.update(modelQuery.where, data, _includes);
348
-
349
- // console.log({ updateResult });
350
- // }
351
-
352
- // const result = {
353
- // success:isNewRecord === false,
354
- // [this.nameSingular]:instance
355
- // };
356
-
357
- // // Send output.
358
- // return Promise.resolve(result);
359
- // }
360
- // catch(error) {
361
- // return Promise.reject(error);
362
- // }
363
- // }
364
-
365
- /*
366
- * @param <UInt> id
367
- */
368
- async deleteOne(params) {
369
- try {
370
- const { id } = Params(params, {
371
- id: null
372
- });
373
-
374
- const count = await this.model.deleteById(id);
375
-
376
- const result = {
377
- success: count > 0,
378
- count: count
379
- };
380
-
381
- // Send output.
382
- return Promise.resolve(result);
383
- }
384
- catch(error) {
385
- return Promise.reject(error);
386
- }
387
- }
388
- }
@@ -1,11 +0,0 @@
1
- const Params = require('nodester/utils/params.util');
2
-
3
-
4
- module.exports = FacadeParams;
5
-
6
- function FacadeParams(
7
- sourceObject={},
8
- defaultValuesList={}
9
- ) {
10
- return Params(sourceObject, defaultValuesList);
11
- }
@@ -1,17 +0,0 @@
1
- // Utils:
2
- const { lowerCaseFirstLetter } = require('nodester/utils/strings.util');
3
-
4
-
5
- module.exports = class ServiceFacade {
6
- constructor(
7
- nameSingular,
8
- namePlural,
9
- ) {
10
- if (!nameSingular || !namePlural) {
11
- throw new Error('"nameSingular" and "namePlural" arguments must be set.');
12
- }
13
-
14
- this.nameSingular = lowerCaseFirstLetter(nameSingular);
15
- this.namePlural = lowerCaseFirstLetter(namePlural);
16
- }
17
- }