nodester 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/Readme.md +33 -39
  2. package/lib/application/index.js +110 -38
  3. package/lib/constants/Operations.js +23 -0
  4. package/lib/controllers/methods/index.js +194 -0
  5. package/lib/controllers/mixins/index.js +222 -0
  6. package/lib/database/connection.js +34 -0
  7. package/lib/database/migration.js +42 -0
  8. package/lib/database/utils.js +19 -0
  9. package/lib/enums/Enum.js +16 -0
  10. package/lib/facades/methods/index.js +173 -0
  11. package/lib/facades/mixins/index.js +111 -0
  12. package/lib/factories/errors/CustomError.js +7 -5
  13. package/lib/factories/responses/html.js +7 -2
  14. package/lib/factories/responses/rest.js +110 -0
  15. package/lib/http/codes/index.js +157 -0
  16. package/lib/{application/http → http}/request.js +6 -30
  17. package/lib/{application/http → http}/response.js +20 -53
  18. package/lib/middlewares/etag/index.js +62 -0
  19. package/lib/middlewares/ql/sequelize/index.js +34 -0
  20. package/lib/middlewares/ql/sequelize/interpreter/ModelsTree.js +121 -0
  21. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +456 -0
  22. package/lib/models/associate.js +17 -0
  23. package/lib/models/define.js +56 -14
  24. package/lib/models/mixins.js +100 -78
  25. package/lib/params/Params.js +37 -0
  26. package/lib/queries/Colander.js +84 -0
  27. package/lib/queries/NodesterQueryParams.js +139 -0
  28. package/lib/queries/traverse.js +311 -0
  29. package/lib/router/handlers.util.js +61 -0
  30. package/lib/router/index.js +386 -0
  31. package/lib/router/route.js +124 -0
  32. package/lib/router/routes.util.js +66 -0
  33. package/lib/stacks/MarkersStack.js +35 -0
  34. package/lib/{application → stacks}/MiddlewareStack.js +47 -13
  35. package/lib/utils/path.util.js +3 -1
  36. package/lib/utils/types.util.js +51 -1
  37. package/lib/validators/dates.js +25 -0
  38. package/lib/validators/numbers.js +14 -0
  39. package/package.json +31 -4
  40. package/tests/index.test.js +7 -2
  41. package/tests/nql.test.js +277 -0
  42. package/docs/App.md +0 -13
  43. package/docs/Queries.md +0 -61
  44. package/docs/Readme.md +0 -2
  45. package/docs/Routing.md +0 -34
  46. package/examples/goal/index.js +0 -23
  47. package/examples/rest/index.js +0 -25
  48. package/examples/rest/node_modules/.package-lock.json +0 -40
  49. package/examples/rest/package-lock.json +0 -72
  50. package/examples/rest/package.json +0 -14
  51. package/lib/constants/ConstantsEnum.js +0 -13
  52. package/lib/controllers/Controller.js +0 -474
  53. package/lib/controllers/JWTController.js +0 -240
  54. package/lib/controllers/ServiceController.js +0 -109
  55. package/lib/controllers/WebController.js +0 -75
  56. package/lib/facades/Facade.js +0 -388
  57. package/lib/facades/FacadeParams.js +0 -11
  58. package/lib/facades/ServiceFacade.js +0 -17
  59. package/lib/facades/jwt.facade.js +0 -273
  60. package/lib/factories/responses/api.js +0 -90
  61. package/lib/models/DisabledRefreshToken.js +0 -68
  62. package/lib/models/Extractor.js +0 -320
  63. package/lib/routers/Default/index.js +0 -143
  64. package/lib/routers/Default/layer.js +0 -50
  65. package/lib/routers/Main/index.js +0 -10
  66. package/lib/routers/Roles/index.js +0 -81
  67. package/lib/utils/params.util.js +0 -19
  68. /package/lib/{application/http → http}/utils.js +0 -0
package/docs/Routing.md DELETED
@@ -1,34 +0,0 @@
1
- # nodester Routing
2
-
3
- 1) Extend default `nodester` router
4
- ```js
5
- const Router = require('nodester/router');
6
-
7
- class apiRouter extends Router {
8
- constructor() {
9
- super(opts);
10
- this.add.controllers(<path_to_your_controllers>);
11
- this.add.controllers(<path_to_your_controllers>, { namespace: 'view' });
12
-
13
- this.add.routes()
14
- }
15
- }
16
- ```
17
-
18
- Root namespace is named `__root`
19
-
20
- 2) Define routes
21
- ```js
22
- module.exports = {
23
- 'GET /posts': { controlledBy: 'PostsController.getMany' },
24
-
25
- // For different namespace:
26
- 'GET /view/posts': { controlledBy: '@view PostsViewController.getMany' },
27
- // Can also be written as:
28
- 'GET /view/posts': {
29
- namespace: 'view',
30
- controller: 'PostsViewController'
31
- action: 'getMany'
32
- },
33
- }
34
- ```
@@ -1,23 +0,0 @@
1
- const nodester = require('nodester');
2
- const db = require('#db');
3
-
4
- // Init.
5
- const app = new nodester();
6
-
7
-
8
- app.set.database(db);
9
-
10
- app.extend('static')
11
-
12
- app.static(<path/>);
13
-
14
- // Markers.
15
- app.add.marker('admin', (req)=>req.token.role === 'admin');
16
-
17
- // Using Markers.
18
- app.only('admin')
19
- .route('get /payments', (req, res, next) => {});
20
-
21
- app.listen(8080, function() {
22
- console.log('listening on port', app.port);
23
- });
@@ -1,25 +0,0 @@
1
- const Nodester = require('nodester');
2
-
3
- // Init.
4
- const app = new Nodester();
5
-
6
- // app.setDatabase();
7
- // app.set('database');
8
-
9
- // app.add('marker', 'GET_M', (req)=>req.method === 'GET');
10
-
11
- app.add.middleware((req, res, next)=>{
12
- console.log('1st');
13
-
14
- res.setHeader("Content-type", "text/html");
15
- res.write("Hello!<br/>...my friend");
16
- res.end();
17
- })
18
-
19
- app.beforeStart(()=>{
20
- console.log('Before start passed!');
21
- });
22
-
23
- app.listen(8080, function() {
24
- console.log('listening on port', app.port);
25
- });
@@ -1,40 +0,0 @@
1
- {
2
- "name": "nodester-example-rest",
3
- "version": "1.0.0",
4
- "lockfileVersion": 2,
5
- "requires": true,
6
- "packages": {
7
- "../..": {
8
- "version": "0.6.1",
9
- "license": "MIT",
10
- "dependencies": {
11
- "accepts": "^1.3.8",
12
- "content-disposition": "^0.5.4",
13
- "content-type": "^1.0.5",
14
- "cookie": "^0.5.0",
15
- "cookie-signature": "^1.2.0",
16
- "debug": "^4.3.4",
17
- "finalhandler": "^1.2.0",
18
- "formidable": "^1.2.6",
19
- "inflection": "^2.0.1",
20
- "proxy-addr": "^2.0.7",
21
- "qs": "^6.11.0",
22
- "sequelize": "^6.6.5",
23
- "serve-static": "^1.15.0",
24
- "slugify": "^1.6.5",
25
- "type-is": "^1.6.18",
26
- "vary": "^1.1.2"
27
- },
28
- "devDependencies": {
29
- "jest": "^29.4.2"
30
- },
31
- "engines": {
32
- "node": ">= 12.17.0"
33
- }
34
- },
35
- "node_modules/nodester": {
36
- "resolved": "../..",
37
- "link": true
38
- }
39
- }
40
- }
@@ -1,72 +0,0 @@
1
- {
2
- "name": "nodester-example-rest",
3
- "version": "1.0.0",
4
- "lockfileVersion": 2,
5
- "requires": true,
6
- "packages": {
7
- "": {
8
- "name": "nodester-example-rest",
9
- "version": "1.0.0",
10
- "license": "MIT",
11
- "dependencies": {
12
- "nodester": "file:../../"
13
- }
14
- },
15
- "../..": {
16
- "version": "0.6.1",
17
- "license": "MIT",
18
- "dependencies": {
19
- "accepts": "^1.3.8",
20
- "content-disposition": "^0.5.4",
21
- "content-type": "^1.0.5",
22
- "cookie": "^0.5.0",
23
- "cookie-signature": "^1.2.0",
24
- "debug": "^4.3.4",
25
- "finalhandler": "^1.2.0",
26
- "formidable": "^1.2.6",
27
- "inflection": "^2.0.1",
28
- "proxy-addr": "^2.0.7",
29
- "qs": "^6.11.0",
30
- "sequelize": "^6.6.5",
31
- "serve-static": "^1.15.0",
32
- "slugify": "^1.6.5",
33
- "type-is": "^1.6.18",
34
- "vary": "^1.1.2"
35
- },
36
- "devDependencies": {
37
- "jest": "^29.4.2"
38
- },
39
- "engines": {
40
- "node": ">= 12.17.0"
41
- }
42
- },
43
- "node_modules/nodester": {
44
- "resolved": "../..",
45
- "link": true
46
- }
47
- },
48
- "dependencies": {
49
- "nodester": {
50
- "version": "file:../..",
51
- "requires": {
52
- "accepts": "^1.3.8",
53
- "content-disposition": "^0.5.4",
54
- "content-type": "^1.0.5",
55
- "cookie": "^0.5.0",
56
- "cookie-signature": "^1.2.0",
57
- "debug": "^4.3.4",
58
- "finalhandler": "^1.2.0",
59
- "formidable": "^1.2.6",
60
- "inflection": "^2.0.1",
61
- "jest": "^29.4.2",
62
- "proxy-addr": "^2.0.7",
63
- "qs": "^6.11.0",
64
- "sequelize": "^6.6.5",
65
- "serve-static": "^1.15.0",
66
- "slugify": "^1.6.5",
67
- "type-is": "^1.6.18",
68
- "vary": "^1.1.2"
69
- }
70
- }
71
- }
72
- }
@@ -1,14 +0,0 @@
1
- {
2
- "name": "nodester-example-rest",
3
- "version": "1.0.0",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "author": "Mark Khramko",
10
- "license": "MIT",
11
- "dependencies": {
12
- "nodester": "file:../../"
13
- }
14
- }
@@ -1,13 +0,0 @@
1
-
2
- module.exports = function ConstantsEnum(constantsList = {}) {
3
- // Set list.
4
- this.list = constantsList;
5
-
6
- // Set getters.
7
- Object.keys(constantsList).forEach(key => {
8
- this[key] = constantsList[key];
9
- });
10
-
11
- // Set constants in static array.
12
- this.asArray = Object.values(constantsList);
13
- }
@@ -1,474 +0,0 @@
1
- // Constants.
2
- const VISITOR = 'visitor';
3
- // Data extractor:
4
- const Extractor = require('nodester/models/Extractor');
5
- // Preprocessors:
6
- const QueryPreprocessor = require('nodester/preprocessors/QueryPreprocessor');
7
- const BodyPreprocessor = require('nodester/preprocessors/BodyPreprocessor');
8
- // Reponse protocol generator.
9
- const APIResponseFactory = require('nodester/factories/responses/api');
10
- // Custom error.
11
- const { Err } = require('nodester/factories/errors');
12
-
13
-
14
- module.exports = class Controller {
15
- constructor({
16
- modelFacade,
17
-
18
- queryPreprocessor,
19
- bodyPreprocessor,
20
-
21
- apiResponseFactory,
22
-
23
- // Options.
24
- withFiles,
25
- }) {
26
- if (!modelFacade) {
27
- throw new Error('"modelFacade" argument is invalid.');
28
- }
29
-
30
- // Main model.
31
- const model = modelFacade?.model;
32
-
33
- // Set main services & utils:
34
- this.extractor = new Extractor(model, { withFiles: !!withFiles });
35
- this.facade = modelFacade;
36
-
37
- // Set preprocessors:
38
- // TODO: includes preprocessor.
39
- this.queryPreprocessor = queryPreprocessor ?? new QueryPreprocessor();
40
- this.bodyPreprocessor = bodyPreprocessor ?? null;
41
-
42
- // Extract plural name of model.
43
- const modelPluralName = model?.options?.name?.plural;
44
- // Set private name of this controller.
45
- this.name = `${modelPluralName ?? '_INVALID_NAME_'}Controller`;
46
-
47
- // Init standard API response factory.
48
- const standardAPIResponseFactory = new APIResponseFactory();
49
- // Set response factory:
50
- this.createOKResponse = apiResponseFactory?.createOKResponse ??
51
- standardAPIResponseFactory.createOKResponse.bind(standardAPIResponseFactory);
52
- this.createErrorResponse = apiResponseFactory?.createErrorResponse ??
53
- standardAPIResponseFactory.createErrorResponse.bind(standardAPIResponseFactory);
54
- }
55
-
56
- processError(error, req, res) {
57
- // Default error message.
58
- let errorMessage = error?.message ?? 'Internal server error';
59
- // Default HTTP status code.
60
- let statusCode = error?.status ?? error?.statusCode ?? 500;
61
- // Error response object.
62
- let errorResponse = {};
63
-
64
- switch(error.name) {
65
- case('Unauthorized'): {
66
- statusCode = 401;
67
- errorResponse.details = { message: 'Unauthorized' };
68
- break;
69
- }
70
- case('NotFound'): {
71
- statusCode = 404;
72
- errorResponse.details = { message: errorMessage };
73
- break;
74
- }
75
- case('ValidationError'): {
76
- statusCode = 406;
77
- errorResponse.details = error?.details;
78
- break;
79
- }
80
- case('ConflictError'): {
81
- statusCode = 409;
82
- errorResponse.details = error?.details ?? error?.message;
83
- break;
84
- }
85
- case('SequelizeUniqueConstraintError'): {
86
- statusCode = 409;
87
- errorResponse.details = error?.errors;
88
- break;
89
- }
90
- case('InternalValidationError'): {
91
- statusCode = 500;
92
- errorResponse.details = { message:'Error' };
93
- break;
94
- }
95
- default: {
96
- errorResponse.details = { message:errorMessage };
97
- break;
98
- }
99
- }
100
-
101
- // Send error response with provided status code.
102
- return this.createErrorResponse({
103
- res,
104
- error: {
105
- ...errorResponse,
106
- code: statusCode
107
- },
108
- status: statusCode
109
- });
110
- }
111
-
112
- /* POST: */
113
- async createOne(req, res) {
114
- try {
115
- // Extract all required info:
116
- const {
117
- // query,
118
- includes
119
- } = await this.extractQuery(req, res);
120
-
121
- // Extract request's body.
122
- const { body } = await this.extractBody(req, res);
123
-
124
- // Extract new instance data.
125
- const data = this.extractor.extractInstanceDataFromObject(
126
- body,
127
- includes,
128
- {
129
- skipIdValidation: true
130
- });
131
-
132
- const result = await this.facade.createOne({ data, includes });
133
-
134
- // Hook.
135
- await this.afterCreateOne(req, res, result);
136
-
137
- return this.createOKResponse({
138
- res,
139
- content: { ...result }
140
- });
141
- }
142
- catch(error) {
143
- console.error(`${ this.name }.createOne error:`, error);
144
- return this.processError(error, req, res);
145
- }
146
- }
147
- /* POST\ */
148
-
149
- /* GET: */
150
- async getOne(req, res) {
151
- try {
152
- // Extract all required info:
153
- const {
154
- query,
155
- includes
156
- } = await this.extractQuery(req, res);
157
-
158
- const params = {
159
- query,
160
- includes,
161
- }
162
-
163
- // If Query or Params contains main identifier (id),
164
- // validate it:
165
- if (!!query.id) {
166
- // Extract main identifier.
167
- const { id } = this.extractor.extractInstanceDataFromObject(
168
- query,
169
- null,
170
- {
171
- skipValidation: true
172
- });
173
- params.id = id;
174
- }
175
- // If Request's Params contains main identifier (id),
176
- // validate it:
177
- else if (!!req.params.id) {
178
- // Extract main identifier.
179
- const { id } = this.extractor.extractInstanceDataFromObject(
180
- req.params,
181
- null,
182
- {
183
- skipValidation: true
184
- });
185
- params.id = id;
186
- }
187
-
188
- const result = await this.facade.getOne(params);
189
-
190
- return this.createOKResponse({
191
- res,
192
- content: { ...result }
193
- });
194
- }
195
- catch(error) {
196
- console.error(`${ this.name }.getOne error:`, error);
197
- return this.processError(error, req, res);
198
- }
199
- }
200
-
201
- async getMany(req, res) {
202
- try {
203
- // Extract all required info:
204
- const {
205
- query,
206
- includes
207
- } = await this.extractQuery(req, res);
208
-
209
- const params = {
210
- query,
211
- includes,
212
- }
213
-
214
- // If Query contains main identifier (id),
215
- // validate it:
216
- if (!!query.id) {
217
- // Extract main identifier.
218
- const { id } = this.extractor.extractInstanceDataFromObject(
219
- query,
220
- null,
221
- {
222
- skipValidation: true
223
- });
224
- params.query.id = id;
225
- }
226
- // If Request's Params contains main identifier (id),
227
- // validate it:
228
- else if (!!req.params.id) {
229
- // Extract main identifier.
230
- const { id } = this.extractor.extractInstanceDataFromObject(
231
- req.params,
232
- null,
233
- {
234
- skipValidation: true
235
- });
236
- params.query.id = id;
237
- }
238
-
239
- const result = await this.facade.getMany(params);
240
-
241
- return this.createOKResponse({
242
- res,
243
- content: { ...result }
244
- });
245
- }
246
- catch(error) {
247
- console.error(`${ this.name }.getMany error:`, error);
248
- return this.processError(error, req, res);
249
- }
250
- }
251
- /* GET\ */
252
-
253
- /* PUT: */
254
- async updateOne(req, res) {
255
- try {
256
- // Extract all required info:
257
- const {
258
- query,
259
- includes
260
- } = await this.extractQuery(req, res);
261
-
262
- // Extract request's body.
263
- const { body } = await this.extractBody(req, res);
264
-
265
- // Init facade params.
266
- const facadeParams = {
267
- query,
268
- includes,
269
- };
270
-
271
- // If request params contain main identifier (id),
272
- // extract & validate it:
273
- if (!!req.params.id) {
274
- // Extract main identifier.
275
- facadeParams.query.id = parseInt(req.params.id);
276
- }
277
- // If Query contains main identifier (id),
278
- // validate it:
279
- else if (!!query.id) {
280
- // Extract main identifier.
281
- const { id } = this.extractor.extractInstanceDataFromObject(
282
- query,
283
- null,
284
- {
285
- skipValidation: true
286
- });
287
- facadeParams.query.id = id;
288
- }
289
- // Extract instance data.
290
- const data = this.extractor.extractInstanceDataFromObject(
291
- body,
292
- includes,
293
- {
294
- skipIdValidation: true,
295
- skipValidation: true
296
- });
297
- facadeParams.data = data;
298
-
299
- const result = await this.facade.updateOne(facadeParams);
300
-
301
- // Hook.
302
- await this.afterUpdateOne(req, res, result);
303
-
304
- return this.createOKResponse({
305
- res,
306
- content: { ...result }
307
- });
308
- }
309
- catch(error) {
310
- console.error(`${ this.name }.updateOne error:`, error);
311
- return this.processError(error, req, res);
312
- }
313
- }
314
-
315
- /* ! Warning !
316
- * Unfinished method
317
- * Do not use!
318
- */
319
- async updateMany(req, res) {
320
- try {
321
- // Extract all required info:
322
- const {
323
- query,
324
- includes
325
- } = await this.extractQuery(req, res);
326
-
327
- // If Query contains main identifier (id),
328
- // extract & validate it:
329
- if (!!query.id) {
330
- // Extract main identifier.
331
- const { id } = this.extractor.extractInstanceDataFromObject(
332
- query,
333
- null,
334
- {
335
- skipValidation: true
336
- });
337
- query.id = id;
338
- }
339
-
340
- // Extract data array:
341
- const data = this.extractor.extractArrayDataFromObject(
342
- req.body,
343
- {
344
- skipIdValidation: true,
345
- skipValidation: true
346
- });
347
-
348
- console.log("Controller.updateMany", { data });
349
-
350
- // TODO: Finish procedure
351
- return;
352
- // const result = await this.facade.updateMany({ data, includes });
353
-
354
- // return this.createOKResponse({
355
- // res,
356
- // content: { ...result }
357
- // });
358
- }
359
- catch(error) {
360
- console.error(`${ this.name }.updateMany error:`, error);
361
- return this.processError(error, req, res);
362
- }
363
- }
364
- /* PUT\ */
365
-
366
- /* DELETE: */
367
- async deleteOne(req, res) {
368
- try {
369
- // Extract main identifier.
370
- const { id } = this.extractor.extractInstanceDataFromObject(
371
- req.params,
372
- null,
373
- {
374
- skipValidation: true
375
- });
376
-
377
- const result = await this.facade.deleteOne({ id });
378
-
379
- // Hook.
380
- await this.afterDeleteOne(req, res, result);
381
-
382
- return this.createOKResponse({
383
- res,
384
- content: { ...result }
385
- });
386
- }
387
- catch(error) {
388
- console.error(`${ this.name }.deleteOne error:`, error);
389
- return this.processError(error, req, res);
390
- }
391
- }
392
- /* DELETE\ */
393
-
394
- /*
395
- * Hooks:
396
- */
397
- async afterCreateOne(
398
- req, res,
399
- facadeResult
400
- ) {
401
- // This method is empty, as it should be overwritten.
402
- const content = { ...facadeResult };
403
- return Promise.resolve(content);
404
- }
405
-
406
- async afterUpdateOne(
407
- req, res,
408
- facadeResult
409
- ) {
410
- // This method is empty, as it should be overwritten.
411
- const content = { ...facadeResult };
412
- return Promise.resolve(content);
413
- }
414
-
415
- async afterDeleteOne(
416
- req, res,
417
- facadeResult
418
- ) {
419
- // This method is empty, as it should be overwritten.
420
- const content = { ...facadeResult };
421
- return Promise.resolve(content);
422
- }
423
- /*
424
- * Hooks\
425
- */
426
-
427
- // Preprocessors:
428
- async extractQuery(req, res) {
429
- try {
430
- // Extract role.
431
- const role = req?.token?.parsed?.role ?? VISITOR;
432
-
433
- // Extract query:
434
- const query = await this.queryPreprocessor.extract(
435
- req,
436
- role,
437
- );
438
- const { includes } = req.query;
439
-
440
- return Promise.resolve({
441
- query,
442
- includes,
443
- });
444
- }
445
- catch(error) {
446
- return Promise.reject(error);
447
- }
448
- }
449
-
450
- async extractBody(req, res, externalBodyObject=null) {
451
- try {
452
- // Extract role.
453
- const role = req?.token?.parsed?.role ?? VISITOR;
454
-
455
- // Extract body:
456
- let body = externalBodyObject ?? req.body;
457
-
458
- if (!!this.bodyPreprocessor) {
459
- body = await this.bodyPreprocessor.extract(
460
- req,
461
- role,
462
- );
463
- }
464
-
465
- return Promise.resolve({
466
- body,
467
- });
468
- }
469
- catch(error) {
470
- return Promise.reject(error);
471
- }
472
- }
473
- // Preprocessors\
474
- }