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
@@ -1,90 +0,0 @@
1
- const ResponseFormats = require('nodester/constants/ResponseFormats');
2
-
3
-
4
- module.exports = class APIResponseFactory {
5
- constructor() {}
6
-
7
- /*
8
- Format for all API responses will be JSON
9
- {
10
- content: {...}
11
- error: {...}
12
- }
13
- Status code is sent in header.
14
-
15
- If error is not present, error should be null.
16
- If error is present, content can be null (But it's not required).
17
- */
18
- createGenericResponse(
19
- options = {
20
- res: null,
21
- status: 200,
22
- content: {},
23
- error: null,
24
- format: ResponseFormats.JSON
25
- }
26
- ) {
27
- try {
28
- const data = {
29
- content: options?.content ?? null,
30
- error: options?.error ?? null
31
- };
32
-
33
- switch(options?.format) {
34
- case ResponseFormats.JSON:
35
- return options?.res.status(options?.status).json(data);
36
- case ResponseFormats.XML:
37
- break;
38
- default: {
39
- const err = new Error("No response format specified.");
40
- throw err;
41
- }
42
- }
43
- }
44
- catch(error) {
45
- const err = new Error(`Could not create generic response: ${error.message}`);
46
- err.name = error?.name;
47
- err.code = error?.code;
48
-
49
- console.error(err);
50
-
51
- throw err;
52
- }
53
- }
54
-
55
- /**
56
- * Sends response with status code 200.
57
- * Should be called on all successful respones.
58
- *
59
- * @param <Object> res
60
- * @param <Object> content
61
- * @param <String> format
62
- */
63
- createOKResponse(options) {
64
-
65
- return this.createGenericResponse({
66
- ...options,
67
- status: 200,
68
- format: options?.format ?? ResponseFormats.JSON
69
- });
70
- }
71
-
72
- /**
73
- * Sends response with provided error code.
74
- * Should be called on all failed respones.
75
- *
76
- * @param <Object> res
77
- * @param <Object> error
78
- * @param <Object> content (optional)
79
- * @param <Int> status
80
- * @param <String> format
81
- */
82
- createErrorResponse(options) {
83
-
84
- return this.createGenericResponse({
85
- ...options,
86
- status: options?.status ?? 500,
87
- format: options?.format ?? ResponseFormats.JSON
88
- });
89
- }
90
- }
@@ -1,68 +0,0 @@
1
- const defineModel = require('./define');
2
- // ORM.
3
- const { DataTypes } = require('sequelize');
4
-
5
-
6
- module.exports = _defineDisabledRefreshToken;
7
-
8
- function _defineDisabledRefreshToken(
9
- database,
10
- roleIds=[],
11
- options={}
12
- ) {
13
-
14
- const fields = {
15
- id: {
16
- type: DataTypes.INTEGER.UNSIGNED,
17
- allowNull: false,
18
- primaryKey: true,
19
- autoIncrement: true,
20
- _autoGenerated: true
21
- },
22
-
23
- token: {
24
- type: DataTypes.STRING,
25
- required: true,
26
- allowNull: false,
27
- unique: true
28
- }
29
- };
30
-
31
- roleIds.forEach(roleId => fields[roleId] = {
32
- type: DataTypes.INTEGER.UNSIGNED,
33
- allowNull: true
34
- });
35
-
36
- const disabledRefreshToken = defineModel(database, 'DisabledRefreshToken',
37
- (DataTypes) => ( fields ),
38
- { ...options }
39
- );
40
-
41
- disabledRefreshToken.createOrFind = function({
42
- token,
43
- ...roleInfo
44
- }) {
45
- const where = {
46
- token
47
- };
48
-
49
- const defaults = {
50
- token: token,
51
- ...roleInfo
52
- };
53
-
54
- const query = {
55
- where,
56
- defaults
57
- };
58
- return this.findOrCreate(query);
59
- }
60
-
61
- disabledRefreshToken.selectAll = function({ token }) {
62
- const where = { token };
63
- const query = { where };
64
- return this.findAll(query);
65
- }
66
-
67
- return disabledRefreshToken;
68
- }
@@ -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,143 +0,0 @@
1
- const Layer = require('./layer');
2
-
3
- // Utils:
4
- const { getType } = require('../../utils/types.util');
5
-
6
-
7
- module.exports = function NodesterDefaultRouter(app=null) {
8
- this.markers = {};
9
- this.layer = new Layer();
10
-
11
- if (!!app) {
12
- this.app = app
13
- }
14
-
15
- // Expose methods:
16
- // Markers:
17
- this.addMarker = _addMarker;
18
- this.getMarker = _getMarker;
19
-
20
- // Routing:
21
- this.handle = _handle;
22
- this.only = _only;
23
- this.route = _route;
24
- }
25
-
26
- /**
27
- * Adds marker to the stack.
28
- * Returns itself for chaining.
29
- * @return {NodesterDefaultRouter}
30
- *
31
- * @alias addMarker
32
- * @public
33
- */
34
- function _addMarker(nameOrSymbol='', fn=()=>{}) {
35
- if (typeof fn !== 'function') {
36
- const err = new TypeError(`Router.addMarker() requires a middleware function but got a ${ getType(fn) }`);
37
- throw err;
38
- }
39
-
40
- const marker = this.getMarker(nameOrSymbol);
41
- if (marker.marker.index > -1) {
42
- const err = new TypeError(`Marker with key ${ nameOrSymbol } is already set.`);
43
- throw err;
44
- }
45
-
46
- this.markers[nameOrSymbol] = fn;
47
-
48
- return this;
49
- }
50
-
51
-
52
- /**
53
- * Tries to find marker's data by provided key.
54
- * @return {Object}
55
- *
56
- * @alias getMarker
57
- * @public
58
- */
59
- function _getMarker(nameOrSymbol='') {
60
- const result = {
61
- marker: {
62
- key: nameOrSymbol,
63
- index: -1,
64
- },
65
- middleware: undefined
66
- }
67
- try {
68
- const keys = Object.keys(this.markers);
69
-
70
- const index = keys.indexOf(nameOrSymbol);
71
- if (keys.indexOf(nameOrSymbol) === -1) {
72
- const err = new Error('NotFound');
73
- throw err;
74
- }
75
-
76
- // Marker found:
77
- result.marker.index = index;
78
- result.middleware = this.markers[nameOrSymbol];
79
- }
80
- catch(error) {
81
- result.marker.index = -1;
82
- }
83
-
84
- return result;
85
- }
86
-
87
- /**
88
- * Start routes pipeline processing.
89
- *
90
- * If no callback is provided, then default error handlers will respond
91
- * in the event of an error bubbling through the stack.
92
- *
93
- * @alias handle
94
- * @public
95
- */
96
- function _handle(req, res, callback) {
97
- const method = req.method;
98
- const requestPath = req.url;
99
-
100
- console.log(method, requestPath);
101
-
102
- let markerName = null;
103
-
104
- // Check if this request satisfies any markers:
105
- const markers = Object.entries(this.markers);
106
- console.log({ markers });
107
-
108
- for (const [marker, fn] of markers) {
109
- const result = fn(req, res, callback);
110
- console.log(result);
111
-
112
- if (result === true) {
113
- markerName = marker;
114
- break;
115
- }
116
- }
117
-
118
- console.log({ markerName });
119
-
120
- return res.send(markerName ?? 'Hi!');
121
- }
122
-
123
-
124
- /**
125
- *
126
- * @alias only
127
- * @public
128
- */
129
- function _only(condition) {
130
- this.layer.push('condition', condition);
131
- // Return layer for chaining.
132
- return this.layer;
133
- }
134
-
135
-
136
- /**
137
- *
138
- * @alias route
139
- * @public
140
- */
141
- function _route() {
142
-
143
- }
@@ -1,50 +0,0 @@
1
-
2
- module.exports = function NodesterDefaultRouterLayer() {
3
- this.conditions = [];
4
- this.routesList = {};
5
-
6
- this.push = _push;
7
- this.route = _route;
8
- }
9
-
10
-
11
- /**
12
- *
13
- * @alias push
14
- * @public
15
- */
16
- function _push(key='condition') {
17
- let args = [...arguments].shift();
18
-
19
- switch(key) {
20
- case 'condition':
21
- // this.conditions[]
22
- break;
23
- case 'route':
24
- const route = args.shift();
25
- this.routesList[route] = args;
26
- break;
27
- break;
28
- }
29
-
30
- return this;
31
- }
32
-
33
-
34
- /**
35
- *
36
- * @alias route
37
- * @public
38
- */
39
- function _route(path='/') {
40
- const middlewares = [...arguments].shift();
41
-
42
- console.log({ path });
43
- }
44
-
45
-
46
- /**
47
- *
48
- * @alias push
49
- * @public
50
- */
@@ -1,10 +0,0 @@
1
- const express = require('express');
2
-
3
-
4
- class MainRouter extends express.Router {
5
- constructor() {
6
- super();
7
- }
8
- }
9
-
10
- module.exports = MainRouter;