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
@@ -0,0 +1,222 @@
1
+ const {
2
+ getOne,
3
+ getMany,
4
+ createOne,
5
+ updateOne,
6
+ deleteOne
7
+ } = require('../methods');
8
+
9
+
10
+ module.exports = {
11
+ withDefaultCRUD: _withDefaultCRUD,
12
+ withDefaultErrorProcessing: _withDefaultErrorProcessing,
13
+ setMethod: _setMethod
14
+ }
15
+
16
+
17
+ /**
18
+ * Sets one of or all of CRUD methods to Controller.
19
+ *
20
+ * @param {Function|Object} controller
21
+ * @param {Object} opts
22
+ * - @param {Function|Object} facade
23
+ * - @param {String} name
24
+ * - @param {Array} only
25
+ *
26
+ * @return {Function|Object} controller
27
+ *
28
+ * @api public
29
+ * @alias withDefaultCRUD
30
+ */
31
+ function _withDefaultCRUD(controller, opts={}) {
32
+ const {
33
+ facade,
34
+
35
+ // Optional:
36
+ name,
37
+ only
38
+ } = opts;
39
+
40
+ if (!controller) {
41
+ const err = new TypeError(`'controller' argument is not provided.`);
42
+ throw err;
43
+ }
44
+
45
+ if (!facade) {
46
+ const err = new TypeError(`'opts.facade' argument is invalid.`);
47
+ throw err;
48
+ }
49
+
50
+ // Set main facade:
51
+ if (facade.constructor.name === 'Function') {
52
+ controller.facade = new facade();
53
+ }
54
+ else {
55
+ controller.facade = facade;
56
+ }
57
+
58
+ // Set model info:
59
+ const model = facade.model;
60
+ // Extract plural name of this model.
61
+ const modelPluralName = model?.options?.name?.plural;
62
+ // Set name of this controller:
63
+ Object.defineProperty(controller, 'name', {
64
+ value: name ?? `${ modelPluralName ?? controller.name }Controller`,
65
+ writable: false
66
+ });
67
+
68
+
69
+ // If only certain methods should be set:
70
+ if (!!only) {
71
+ for (const selectedMethod of only) {
72
+ switch(selectedMethod) {
73
+ case 'getOne':
74
+ controller.getOne = getOne.bind(controller);
75
+ break;
76
+ case 'getMany':
77
+ controller.getMany = getMany.bind(controller);
78
+ break;
79
+ case 'createOne':
80
+ controller.createOne = createOne.bind(controller);
81
+ break;
82
+ case 'updateOne':
83
+ controller.updateOne = updateOne.bind(controller);
84
+ break;
85
+ case 'deleteOne':
86
+ controller.deleteOne = deleteOne.bind(controller);
87
+ break;
88
+
89
+ default:
90
+ break;
91
+ }
92
+ }
93
+ }
94
+ // Or set all methods:
95
+ else {
96
+ controller.getOne = getOne.bind(controller);
97
+ controller.getMany = getMany.bind(controller);
98
+ controller.createOne = createOne.bind(controller);
99
+ controller.updateOne = updateOne.bind(controller);
100
+ controller.deleteOne = deleteOne.bind(controller);
101
+
102
+ // Set empty hooks:
103
+ controller.afterGetOne = async () => {};
104
+ controller.afterGetMany = async () => {};
105
+ controller.afterCreateOne = async () => {};
106
+ controller.afterUpdateOne = async () => {};
107
+ controller.afterDeleteOe = async () => {};
108
+ }
109
+
110
+ // TODO: remove.
111
+ controller.respondOk = (res, data) => res.json(data);
112
+ controller.respondNotOk = (res, data) => { res.status(data.status); res.json(data); };
113
+
114
+ return controller;
115
+ }
116
+
117
+
118
+ /**
119
+ * Sets default error responses to Controller.
120
+ *
121
+ * @param {Function|Object} controller
122
+ * @param {Object} opts
123
+ *
124
+ * @return {Function|Object} controller
125
+ *
126
+ * @api public
127
+ * @alias withDefaultErrorProcessing
128
+ */
129
+ function _withDefaultErrorProcessing(controller, opts={}) {
130
+ if (!controller) {
131
+ const err = new TypeError(`'controller' argument is not provided.`);
132
+ throw err;
133
+ }
134
+
135
+ // Set processError:
136
+ controller.processError = function (error, req, res) {
137
+
138
+ // Default error message.
139
+ let errorMessage = error?.message ?? 'Internal server error';
140
+ // Default HTTP status code.
141
+ let statusCode = error?.status ?? error?.statusCode ?? 500;
142
+ // Error response object.
143
+ let errorResponse = {};
144
+
145
+ switch(error.name) {
146
+ case('Unauthorized'): {
147
+ statusCode = 401;
148
+ errorResponse.details = { message: 'Unauthorized' };
149
+ break;
150
+ }
151
+ case('NotFound'): {
152
+ statusCode = 404;
153
+ errorResponse.details = { message: errorMessage };
154
+ break;
155
+ }
156
+ case('ValidationError'): {
157
+ statusCode = 406;
158
+ errorResponse.details = error?.details;
159
+ break;
160
+ }
161
+ case('ConflictError'): {
162
+ statusCode = 409;
163
+ errorResponse.details = error?.details ?? error?.message;
164
+ break;
165
+ }
166
+ case('SequelizeUniqueConstraintError'): {
167
+ statusCode = 409;
168
+ errorResponse.details = error?.errors;
169
+ break;
170
+ }
171
+ case('InternalValidationError'): {
172
+ statusCode = 500;
173
+ errorResponse.details = { message:'Error' };
174
+ break;
175
+ }
176
+ default: {
177
+ errorResponse.details = { message: errorMessage };
178
+ break;
179
+ }
180
+ }
181
+
182
+ // Send error response with provided status code:
183
+ const data = {
184
+ error: {
185
+ ...errorResponse,
186
+ code: statusCode
187
+ },
188
+ status: statusCode
189
+ }
190
+
191
+ if (!!this.respondNotOk) {
192
+ return this.respondNotOk(res, data);
193
+ }
194
+
195
+ // Barebones response:
196
+ res.status(statusCode);
197
+ res.json(data);
198
+ }
199
+
200
+ return controller;
201
+ }
202
+
203
+
204
+ /**
205
+ * Sets one of CRUD methods to Controller.
206
+ *
207
+ * @param {Function|Object} controller
208
+ * @param {Object} opts
209
+ *
210
+ * @return {Function|Object} controller
211
+ *
212
+ * @api public
213
+ * @alias withDefaultCRUD
214
+ */
215
+ function _setMethod(controller,) {
216
+ if (!controller) {
217
+ const err = new TypeError(`'controller' argument is not provided.`);
218
+ throw err;
219
+ }
220
+
221
+
222
+ }
@@ -0,0 +1,34 @@
1
+ // ORM.
2
+ const Sequelize = require('sequelize');
3
+
4
+
5
+ module.exports = {
6
+ buildConnection: _buildConnection
7
+ };
8
+
9
+ function _buildConnection(opts={}) {
10
+
11
+
12
+ const dbName = opts.name;
13
+ const username = opts.username;
14
+ const password = opts.password;
15
+
16
+
17
+ const connection = new Sequelize(
18
+ dbName,
19
+ username,
20
+ password,
21
+ {
22
+ host: opts.host,
23
+ port: opts.port,
24
+ dialect: opts.dialect,
25
+ pool: opts.pool,
26
+ charset: opts.charset,
27
+ collate: opts.collate,
28
+ timestamps: opts.timestamps,
29
+ logging: opts.logging
30
+ }
31
+ );
32
+
33
+ return connection;
34
+ }
@@ -0,0 +1,42 @@
1
+ const { associateModels } = require('./utils');
2
+
3
+
4
+ module.exports = {
5
+ migrate: _migrate
6
+ }
7
+
8
+ async function _migrate(databaseConnection, force=false) {
9
+ try {
10
+ // Validation of 'force' parameter.
11
+ if (typeof force !== 'boolean') {
12
+ const err = new Error('Wrong "force" parameter; must be boolean.');
13
+ throw err;
14
+ }
15
+
16
+ // Test connection.
17
+ await databaseConnection.authenticate();
18
+
19
+
20
+ const models = databaseConnection.models;
21
+ const modelNames = Object.keys(models);
22
+ console.info('Models to sync:', modelNames);
23
+ console.info('Forcefully?', force);
24
+ console.info('Syncing...\n');
25
+ await associateModels(models);
26
+ await databaseConnection.sync({ force });
27
+ console.info('Successful migration!');
28
+
29
+ const output = {
30
+ synced: true,
31
+ modelNames: modelNames,
32
+ models: models
33
+ }
34
+ return Promise.resolve(output);
35
+ }
36
+ catch(error) {
37
+ console.error('Migration failed!');
38
+ console.error(error);
39
+ return Promise.reject(error);
40
+ }
41
+ }
42
+
@@ -0,0 +1,19 @@
1
+
2
+ module.exports = {
3
+ associateModels: _associateModels
4
+ }
5
+
6
+ async function _associateModels(models) {
7
+ return new Promise((resolve, reject) => {
8
+ try {
9
+ Object.keys(models).map(modelName => (
10
+ models[modelName].associate(models)
11
+ ));
12
+
13
+ return resolve(models);
14
+ }
15
+ catch(error) {
16
+ reject(error);
17
+ }
18
+ });
19
+ }
@@ -0,0 +1,16 @@
1
+
2
+ module.exports = Enum;
3
+
4
+ function Enum(constantsList = {}, writable=false) {
5
+ const def = (key, value) => Object.defineProperty(this, key, { value, writable: !!writable });
6
+
7
+ // Set list.
8
+ def('list', constantsList);
9
+
10
+ // Set getters:
11
+ Object.keys(constantsList)
12
+ .forEach(key => def(key, constantsList[key]) );
13
+
14
+ // Set constants in static array.
15
+ def('asArray', Object.values(constantsList) );
16
+ }
@@ -0,0 +1,173 @@
1
+ const Params = require('nodester/params');
2
+
3
+
4
+ module.exports = {
5
+ getOne: _getOne,
6
+ getMany: _getMany,
7
+ createOne: _createOne,
8
+ updateOne: _updateOne,
9
+ deleteOne: _deleteOne
10
+ }
11
+
12
+
13
+ /*
14
+ *
15
+ * @param {Object} params
16
+ *
17
+ * @alias getOne
18
+ * @api public
19
+ */
20
+ async function _getOne(params) {
21
+ try {
22
+ const {
23
+ query
24
+ } = Params(params, {
25
+ query: {}
26
+ });
27
+
28
+ const instance = await this.model.findOne(query);
29
+
30
+ const result = {
31
+ [this.modelName.singular]: instance,
32
+ count: 0 + instance !== null
33
+ }
34
+ return Promise.resolve(result);
35
+ }
36
+ catch(error) {
37
+ return Promise.reject(error);
38
+ }
39
+ }
40
+
41
+
42
+ /*
43
+ *
44
+ * @param {Object} params
45
+ *
46
+ * @alias getMany
47
+ * @api public
48
+ */
49
+ async function _getMany(params) {
50
+ try {
51
+ const {
52
+ query
53
+ } = Params(params, {
54
+ query: {}
55
+ });
56
+
57
+ const instances = await this.model.findAll(query);
58
+
59
+ const result = {
60
+ [this.modelName.plural]: instances,
61
+ count: instances.length
62
+ }
63
+ return Promise.resolve(result);
64
+ }
65
+ catch(error) {
66
+ return Promise.reject(error);
67
+ }
68
+ }
69
+
70
+
71
+ /*
72
+ *
73
+ * @param {Object} params
74
+ *
75
+ * @alias createOne
76
+ * @api public
77
+ */
78
+ async function _createOne(params) {
79
+ try {
80
+ const {
81
+ data,
82
+ includes,
83
+ } = Params(params, {
84
+ data: null,
85
+ includes: null,
86
+ });
87
+
88
+ const instance = await this.model.create({ ...data }, {
89
+ include: this.model.getIncludesList(data)
90
+ });
91
+
92
+ // If includes are set, "find" this record with includes:
93
+ if (!!includes && includes?.length > 0) {
94
+ await instance.reload({ include: includes });
95
+ }
96
+
97
+ const result = {
98
+ [this.modelName.singular]: instance,
99
+ count: instance === null ? 0 : 1
100
+ }
101
+
102
+ // Call after create.
103
+ await this.afterCreateOne(instance, params, result);
104
+
105
+ return Promise.resolve(result);
106
+ }
107
+ catch(error) {
108
+ return Promise.reject(error);
109
+ }
110
+ }
111
+
112
+
113
+ /*
114
+ *
115
+ * @param {Object} params
116
+ *
117
+ * @alias updateOne
118
+ * @api public
119
+ */
120
+ async function _updateOne(params) {
121
+ try {
122
+ const {
123
+ query,
124
+ data
125
+ } = Params(params, {
126
+ query: {},
127
+ data: null
128
+ });
129
+
130
+ const updateResult = await this.model.updateOne(query.where, data);;
131
+
132
+ const [ isNewRecord, instance ] = updateResult;
133
+
134
+ const result = {
135
+ success: isNewRecord === false,
136
+ [this.modelName.singular]: instance,
137
+ count: !!instance ? 1 : 0
138
+ }
139
+ return Promise.resolve(result);
140
+ }
141
+ catch(error) {
142
+ return Promise.reject(error);
143
+ }
144
+ }
145
+
146
+
147
+ /*
148
+ *
149
+ * @param {Object} params
150
+ *
151
+ * @alias deleteOne
152
+ * @api public
153
+ */
154
+ async function _deleteOne(params) {
155
+ try {
156
+ const {
157
+ query
158
+ } = Params(params, {
159
+ query: null
160
+ });
161
+
162
+ const count = await this.model.deleteOne(query);
163
+
164
+ const result = {
165
+ success: count > 0,
166
+ count: count
167
+ };
168
+ return Promise.resolve(result);
169
+ }
170
+ catch(error) {
171
+ return Promise.reject(error);
172
+ }
173
+ }
@@ -0,0 +1,111 @@
1
+ const {
2
+ getOne,
3
+ getMany,
4
+ createOne,
5
+ updateOne,
6
+ deleteOne
7
+ } = require('../methods');
8
+ // Utils,
9
+ const { lowerCaseFirstLetter } = require('nodester/utils/strings');
10
+
11
+
12
+ module.exports = {
13
+ withDefaultCRUD: _withDefaultCRUD,
14
+ }
15
+
16
+ /**
17
+ * Sets one of or all of CRUD methods to Facade.
18
+ *
19
+ * @param {Function|Object} facade
20
+ * @param {Object} opts
21
+ * - @param {Function|Object} model
22
+ * - @param {String} name
23
+ * - @param {Array} only
24
+ *
25
+ * @return {Function|Object} facade
26
+ *
27
+ * @api public
28
+ * @alias withDefaultCRUD
29
+ */
30
+ function _withDefaultCRUD(facade, opts={}) {
31
+ const {
32
+ model,
33
+
34
+ // Optional:
35
+ name,
36
+ only
37
+ } = opts;
38
+
39
+ if (!facade) {
40
+ const err = new TypeError(`'facade' argument is not provided.`);
41
+ throw err;
42
+ }
43
+
44
+ // Set model info:
45
+ // Set model:
46
+ Object.defineProperty(facade, 'model', {
47
+ value: model,
48
+ writable: false
49
+ });
50
+
51
+ // Model name:
52
+ const modelName = model.options.name;
53
+ Object.defineProperty(facade, 'modelName', {
54
+ value: {
55
+ singular: lowerCaseFirstLetter(modelName.singular),
56
+ plural: lowerCaseFirstLetter(modelName.plural)
57
+ },
58
+ writable: false
59
+ });
60
+
61
+
62
+ // Set name of this facade:
63
+ Object.defineProperty(facade, 'name', {
64
+ value: name ?? `${ modelName.plural ?? facade.name }Facade`,
65
+ writable: false
66
+ });
67
+
68
+
69
+ // If only certain methods should be set:
70
+ if (!!only) {
71
+ for (const selectedMethod of only) {
72
+ switch(selectedMethod) {
73
+ case 'getOne':
74
+ facade.getOne = getOne.bind(facade);
75
+ break;
76
+ case 'getMany':
77
+ facade.getMany = getMany.bind(facade);
78
+ break;
79
+ case 'createOne':
80
+ facade.createOne = createOne.bind(facade);
81
+ break;
82
+ case 'updateOne':
83
+ facade.updateOne = updateOne.bind(facade);
84
+ break;
85
+ case 'deleteOne':
86
+ facade.deleteOne = deleteOne.bind(facade);
87
+ break;
88
+
89
+ default:
90
+ break;
91
+ }
92
+ }
93
+ }
94
+ // Or set all methods:
95
+ else {
96
+ facade.getOne = getOne.bind(facade);
97
+ facade.getMany = getMany.bind(facade);
98
+ facade.createOne = createOne.bind(facade);
99
+ facade.updateOne = updateOne.bind(facade);
100
+ facade.deleteOne = deleteOne.bind(facade);
101
+
102
+ // Set empty hooks:
103
+ facade.afterGetOne = async () => {};
104
+ facade.afterGetMany = async () => {};
105
+ facade.afterCreateOne = async () => {};
106
+ facade.afterUpdateOne = async () => {};
107
+ facade.afterDeleteOe = async () => {};
108
+ }
109
+
110
+ return facade;
111
+ }
@@ -1,6 +1,8 @@
1
- class CustomError extends Error {
1
+
2
+ module.exports = class CustomError extends Error {
2
3
  constructor(message) {
3
4
  super(message);
5
+
4
6
  this.name = this.constructor.name;
5
7
  this.status = 500;
6
8
 
@@ -14,9 +16,9 @@ class CustomError extends Error {
14
16
  this.status = originalError?.status ?? this.status;
15
17
 
16
18
  // Append stack from original error.
17
- const messageLines = (this.message.match(/\n/g)||[]).length + 1
18
- this.stack = this.stack.split('\n').slice(0, messageLines+1).join('\n') + '\n' + originalError.stack;
19
+ const linesCount = (this.message.match(/\n/g)||[]).length + 1;
20
+ this.stack = this.stack.split('\n')
21
+ .slice(0, linesCount+1)
22
+ .join('\n') + '\n' + originalError.stack;
19
23
  }
20
24
  }
21
-
22
- module.exports = CustomError;
@@ -1,7 +1,12 @@
1
- const Params = require('nodester/facades/FacadeParams');
1
+ /*
2
+ * WARNING: Unfinihsed. Do not use!
3
+ *
4
+ */
2
5
 
6
+ const Params = require('../../params/Params');
3
7
 
4
- module.exports = class WebResponseFactory {
8
+
9
+ module.exports = class HtmlResponseFactory {
5
10
  constructor() {}
6
11
 
7
12
  /**