lesgo 0.6.0 → 0.7.0

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 (39) hide show
  1. package/README.md +8 -2
  2. package/bin/lesgo-scripts.sh +2 -0
  3. package/package.json +8 -4
  4. package/src/middlewares/__tests__/errorHttpResponseMiddleware.spec.js +33 -16
  5. package/src/middlewares/__tests__/gzipHttpResponse.spec.js +6 -6
  6. package/src/middlewares/__tests__/normalizeHttpRequestMiddleware.spec.js +38 -0
  7. package/src/middlewares/__tests__/successHttpResponseMiddleware.spec.js +28 -12
  8. package/src/middlewares/errorHttpResponseMiddleware.js +10 -3
  9. package/src/middlewares/gzipHttpResponse.js +1 -1
  10. package/src/middlewares/normalizeHttpRequestMiddleware.js +21 -2
  11. package/src/middlewares/successHttpResponseMiddleware.js +9 -2
  12. package/src/middlewares/verifyJwtMiddleware.js +4 -2
  13. package/src/services/AuroraDbRDSProxyService.js +183 -0
  14. package/src/services/AuroraDbService.js +39 -5
  15. package/src/services/DynamoDbService.js +10 -8
  16. package/src/services/FirebaseAdminService.js +10 -7
  17. package/src/services/__tests__/AuroraDbRDSProxyService.spec.js +278 -0
  18. package/src/services/__tests__/AuroraDbService.spec.js +54 -0
  19. package/src/services/__tests__/LengthAwarePaginator.spec.js +180 -0
  20. package/src/services/__tests__/Paginator.spec.js +383 -0
  21. package/src/services/pagination/LengthAwarePaginator.js +49 -0
  22. package/src/services/pagination/Paginator.js +254 -0
  23. package/src/utils/__mocks__/db.js +109 -0
  24. package/src/utils/__tests__/db.spec.js +40 -1
  25. package/src/utils/__tests__/getJwtSubFromAuthHeader.spec.js +20 -0
  26. package/src/utils/__tests__/isDecimal.spec.js +12 -0
  27. package/src/utils/__tests__/isEmail.spec.js +28 -0
  28. package/src/utils/__tests__/prepSQLInsertParams.spec.js +46 -0
  29. package/src/utils/__tests__/prepSQLUpdateParams.spec.js +36 -0
  30. package/src/utils/__tests__/validateFields.spec.js +183 -0
  31. package/src/utils/db.js +13 -2
  32. package/src/utils/getJwtSubFromAuthHeader.js +18 -0
  33. package/src/utils/index.js +2 -0
  34. package/src/utils/isDecimal.js +2 -0
  35. package/src/utils/isEmail.js +15 -0
  36. package/src/utils/logger.js +1 -4
  37. package/src/utils/prepSQLInsertParams.js +21 -0
  38. package/src/utils/prepSQLUpdateParams.js +25 -0
  39. package/src/utils/validateFields.js +75 -0
@@ -0,0 +1,183 @@
1
+ import LesgoException from '../../exceptions/LesgoException';
2
+ import validateFields from '../validateFields';
3
+
4
+ const FILE = 'Utils/validateFields';
5
+
6
+ const params = {
7
+ Id: 99,
8
+ name: 'name',
9
+ email: 'email@mail.com',
10
+ roles: { admin: 'admin' },
11
+ listItem: ['apple', 'banana'],
12
+ status: 'active',
13
+ decimalCheck: 1.99,
14
+ totalRecord: 99,
15
+ };
16
+
17
+ const validFields = [
18
+ { key: 'Id', type: 'number', required: true },
19
+ { key: 'name', type: 'string', required: true },
20
+ { key: 'email', type: 'email', required: true },
21
+ { key: 'roles', type: 'object', required: true },
22
+ { key: 'listItem', type: 'array', required: true },
23
+ {
24
+ key: 'status',
25
+ type: 'enum',
26
+ enumValues: ['active', 'inactive'],
27
+ required: true,
28
+ },
29
+ { key: 'decimalCheck', type: 'decimal', required: true },
30
+ { key: 'totalRecord', type: 'number', required: true },
31
+ ];
32
+
33
+ describe('test Utils/validateFields', () => {
34
+ it('should return validated fields', () => {
35
+ expect(validateFields(params, validFields)).toMatchObject(params);
36
+ });
37
+
38
+ it('should throw missing required field when not present', () => {
39
+ const newParams = { ...params };
40
+ delete newParams.email;
41
+
42
+ expect(() => validateFields(newParams, validFields)).toThrow(
43
+ new LesgoException(
44
+ "Missing required 'email'",
45
+ `${FILE}::MISSING_REQUIRED_EMAIL}`
46
+ )
47
+ );
48
+ });
49
+
50
+ it('should throw invalid type when non-string value check', () => {
51
+ const newParams = { ...params, name: 123 };
52
+
53
+ expect(() => validateFields(newParams, validFields)).toThrow(
54
+ new LesgoException(
55
+ `Invalid type for 'name', expecting 'string'`,
56
+ `${FILE}::INVALID_TYPE_NAME`
57
+ )
58
+ );
59
+ });
60
+
61
+ it('should throw invalid type when non-object value check', () => {
62
+ const newParams = { ...params, roles: 1597929335 };
63
+
64
+ expect(() => validateFields(newParams, validFields)).toThrow(
65
+ new LesgoException(
66
+ `Invalid type for 'roles', expecting 'object'`,
67
+ `${FILE}::INVALID_TYPE_ROLES`
68
+ )
69
+ );
70
+ });
71
+
72
+ it('should throw invalid type when non-number value check', () => {
73
+ const newParams = { ...params, Id: '123' };
74
+
75
+ expect(() => validateFields(newParams, validFields)).toThrow(
76
+ new LesgoException(
77
+ `Invalid type for 'Id', expecting 'number'`,
78
+ `${FILE}::INVALID_TYPE_ID`
79
+ )
80
+ );
81
+ });
82
+
83
+ it('should throw invalid type when non-array value check', () => {
84
+ const newParams = { ...params, listItem: { created_at: 1597929335 } };
85
+
86
+ expect(() => validateFields(newParams, validFields)).toThrow(
87
+ new LesgoException(
88
+ `Invalid type for 'listItem', expecting 'array'`,
89
+ `${FILE}::INVALID_TYPE_LISTITEM`
90
+ )
91
+ );
92
+ });
93
+
94
+ it('should throw required when array value is empty but required', () => {
95
+ const newParams = { ...params, listItem: [] };
96
+
97
+ expect(() => validateFields(newParams, validFields)).toThrow(
98
+ new LesgoException(
99
+ `Missing required 'listItem'`,
100
+ `${FILE}::MISSING_REQUIRED_LISTITEM`
101
+ )
102
+ );
103
+ });
104
+
105
+ it('should not throw invalid type when array value is empty and not required', () => {
106
+ const newParams = { ...params, listItem: [] };
107
+ const newValidFields = [
108
+ { key: 'listItem', type: 'array', required: false },
109
+ ];
110
+
111
+ expect(validateFields(newParams, newValidFields)).toMatchObject({
112
+ listItem: [],
113
+ });
114
+ });
115
+
116
+ it('should throw invalid type when non-enum value check', async () => {
117
+ const newParams = { ...params, status: 'private' };
118
+
119
+ expect(() => validateFields(newParams, validFields)).toThrow(
120
+ new LesgoException(
121
+ `Invalid type for 'status', expecting 'enum'`,
122
+ `${FILE}::INVALID_TYPE_STATUS`
123
+ )
124
+ );
125
+ });
126
+
127
+ it('should return only valid and allowed fields when other fields are received', () => {
128
+ const newParams = { ...params, someOtherKey: 'someOtherValue' };
129
+ const validated = validateFields(newParams, validFields);
130
+
131
+ expect(validated).toMatchObject(params);
132
+ expect(validated.Id).toBeDefined();
133
+ expect(validated.someOtherKey).toBeUndefined();
134
+ });
135
+
136
+ it('should return success with validated data for non-required fields', () => {
137
+ const newParams = { ...params };
138
+ delete newParams.listItem;
139
+
140
+ validFields[4].required = false;
141
+ const validated = validateFields(newParams, validFields);
142
+
143
+ expect(validated).toMatchObject(newParams);
144
+ expect(validated.Id).toBeDefined();
145
+ expect(validated.listItem).toBeUndefined();
146
+
147
+ validFields[4].required = true;
148
+ });
149
+
150
+ it('should return success with validated data for 0 number', () => {
151
+ const newParams = { ...params, totalRecord: 0 };
152
+ const validated = validateFields(newParams, validFields);
153
+
154
+ expect(validated).toMatchObject(newParams);
155
+ expect(validated.totalRecord).toBeDefined();
156
+ expect(validated.totalRecord).toEqual(0);
157
+ });
158
+
159
+ it('should return success with validated data for number without required', () => {
160
+ const newParams = { ...params };
161
+ validFields[7].required = false;
162
+
163
+ const validated = validateFields(newParams, validFields);
164
+
165
+ expect(validated).toMatchObject(newParams);
166
+ expect(validated.totalRecord).toBeDefined();
167
+ expect(validated.totalRecord).toEqual(99);
168
+
169
+ validFields[7].required = true;
170
+ });
171
+
172
+ it('should return error with missing required number', () => {
173
+ const newParams = { ...params };
174
+ delete newParams.totalRecord;
175
+
176
+ expect(() => validateFields(newParams, validFields)).toThrow(
177
+ new LesgoException(
178
+ `Missing required 'totalRecord'`,
179
+ `${FILE}::MISSING_REQUIRED_TOTALRECORD`
180
+ )
181
+ );
182
+ });
183
+ });
package/src/utils/db.js CHANGED
@@ -1,6 +1,17 @@
1
- import config from 'Config/db'; // eslint-disable-line import/no-unresolved
1
+ import dbConfig from 'Config/db'; // eslint-disable-line import/no-unresolved
2
+ import AuroraDbRDSProxyService from '../services/AuroraDbRDSProxyService';
2
3
  import AuroraDbService from '../services/AuroraDbService';
3
4
 
4
- const db = new AuroraDbService(config);
5
+ /* eslint-disable-next-line import/no-mutable-exports */
6
+ let db;
7
+
8
+ if (dbConfig.default === 'rdsProxy' || dbConfig.default === 'rdsProxyRead') {
9
+ db = new AuroraDbRDSProxyService(dbConfig.connections[dbConfig.default]);
10
+ } else if (dbConfig.default === 'dataApi') {
11
+ db = new AuroraDbService(dbConfig.connections[dbConfig.default]);
12
+ } else {
13
+ // @deprecated
14
+ db = new AuroraDbService(dbConfig);
15
+ }
5
16
 
6
17
  export default db;
@@ -0,0 +1,18 @@
1
+ import isEmpty from './isEmpty';
2
+
3
+ export const getTokenData = authHeader => {
4
+ try {
5
+ return JSON.parse(Buffer.from(authHeader.split('.')[1], 'base64'));
6
+ } catch (err) {
7
+ return {};
8
+ }
9
+ };
10
+
11
+ export default authHeader => {
12
+ if (isEmpty(authHeader)) {
13
+ return null;
14
+ }
15
+
16
+ const data = getTokenData(authHeader);
17
+ return data.sub || null;
18
+ };
@@ -4,6 +4,7 @@ import logger from './logger';
4
4
  import { getObject } from './objectStore';
5
5
  import { dispatch } from './queue';
6
6
  import isEmpty from './isEmpty';
7
+ import isDecimal from './isDecimal';
7
8
  import crypto from './crypto';
8
9
  import generateUid from './generateUid';
9
10
  import db from './db';
@@ -17,6 +18,7 @@ export {
17
18
  getObject,
18
19
  logger,
19
20
  isEmpty,
21
+ isDecimal,
20
22
  crypto,
21
23
  generateUid,
22
24
  db,
@@ -0,0 +1,2 @@
1
+ export default number =>
2
+ typeof number !== 'string' && number.toString().indexOf('.') !== -1;
@@ -0,0 +1,15 @@
1
+ import isEmpty from './isEmpty';
2
+ import LesgoException from '../exceptions/LesgoException';
3
+
4
+ export default email => {
5
+ const pattern = /^[\w\-\.\+]+\@[a-zA-Z0-9\.\-]+\.[a-zA-z0-9]{2,5}$/; // eslint-disable-line no-useless-escape
6
+
7
+ if (isEmpty(email)) {
8
+ throw new LesgoException(
9
+ 'Empty parameter supplied',
10
+ 'IS_EMAIL_EMPTY_PARAMETER'
11
+ );
12
+ }
13
+
14
+ return pattern.test(email);
15
+ };
@@ -4,10 +4,7 @@ import LoggerService from '../services/LoggerService';
4
4
  const transports = [
5
5
  {
6
6
  logType: 'console',
7
- level:
8
- app.env === 'local' && /* istanbul ignore next */ app.debug
9
- ? /* istanbul ignore next */ 'debug'
10
- : 'info',
7
+ level: /* istanbul ignore next */ app.debug ? 'debug' : 'info',
11
8
  config: {
12
9
  getCreatedAt: true,
13
10
  tags: {
@@ -0,0 +1,21 @@
1
+ export default (params, columns) => {
2
+ const insertFields = {};
3
+ const insertColumns = [];
4
+ const insertValues = [];
5
+
6
+ columns.forEach(column => {
7
+ const { key } = column;
8
+
9
+ if (typeof params[key] !== 'undefined') {
10
+ insertColumns.push(key);
11
+ insertValues.push([`:${key}`]);
12
+ insertFields[key] = params[key];
13
+ }
14
+ });
15
+
16
+ return {
17
+ insertColumns: insertColumns.join(','),
18
+ insertValues: insertValues.join(','),
19
+ insertFields,
20
+ };
21
+ };
@@ -0,0 +1,25 @@
1
+ export default (params, columns) => {
2
+ const updateFields = {};
3
+ const updateColumnValues = [];
4
+ let wherePrimaryKey = '';
5
+
6
+ columns.forEach(column => {
7
+ const { key } = column;
8
+
9
+ if (typeof params[key] !== 'undefined') {
10
+ if (key === 'id') {
11
+ wherePrimaryKey = `${key}=:${key}`;
12
+ updateFields[key] = params[key];
13
+ } else {
14
+ updateColumnValues.push([`${key}=:${key}`]);
15
+ updateFields[key] = params[key];
16
+ }
17
+ }
18
+ });
19
+
20
+ return {
21
+ updateColumnValues: updateColumnValues.join(','),
22
+ wherePrimaryKey,
23
+ updateFields,
24
+ };
25
+ };
@@ -0,0 +1,75 @@
1
+ import LesgoException from '../exceptions/LesgoException';
2
+ import isEmail from './isEmail';
3
+ import isDecimal from './isDecimal';
4
+
5
+ const FILE = 'Utils/validateFields';
6
+
7
+ export default (params, validFields) => {
8
+ const validated = {};
9
+
10
+ validFields.forEach(field => {
11
+ if (field.required) {
12
+ if (typeof params[field.key] === 'object') {
13
+ if (
14
+ Array.isArray(params[field.key]) &&
15
+ params[field.key].length === 0
16
+ ) {
17
+ throw new LesgoException(
18
+ `Missing required '${field.key}'`,
19
+ `${FILE}::MISSING_REQUIRED_${field.key.toUpperCase()}`,
20
+ 500,
21
+ { field }
22
+ );
23
+ }
24
+ }
25
+
26
+ if (!params[field.key]) {
27
+ if (typeof params[field.key] !== 'number') {
28
+ throw new LesgoException(
29
+ `Missing required '${field.key}'`,
30
+ `${FILE}::MISSING_REQUIRED_${field.key.toUpperCase()}`,
31
+ 500,
32
+ { field }
33
+ );
34
+ }
35
+ }
36
+ }
37
+
38
+ if (
39
+ (field.type === 'string' &&
40
+ typeof params[field.key] !== 'undefined' &&
41
+ typeof params[field.key] !== 'string') ||
42
+ (field.type === 'object' &&
43
+ typeof params[field.key] !== 'undefined' &&
44
+ typeof params[field.key] !== 'object') ||
45
+ (field.type === 'number' &&
46
+ typeof params[field.key] !== 'undefined' &&
47
+ typeof params[field.key] !== 'number') ||
48
+ (field.type === 'decimal' &&
49
+ typeof params[field.key] !== 'undefined' &&
50
+ !isDecimal(params[field.key])) ||
51
+ (field.type === 'email' &&
52
+ typeof params[field.key] !== 'undefined' &&
53
+ !isEmail(params[field.key])) ||
54
+ (field.type === 'array' &&
55
+ typeof params[field.key] !== 'undefined' &&
56
+ !Array.isArray(params[field.key])) ||
57
+ (field.type === 'enum' &&
58
+ typeof params[field.key] !== 'undefined' &&
59
+ !field.enumValues.includes(params[field.key]))
60
+ ) {
61
+ throw new LesgoException(
62
+ `Invalid type for '${field.key}', expecting '${field.type}'`,
63
+ `${FILE}::INVALID_TYPE_${field.key.toUpperCase()}`,
64
+ 500,
65
+ { field, value: params[field.key] }
66
+ );
67
+ }
68
+
69
+ if (typeof params[field.key] !== 'undefined') {
70
+ validated[field.key] = params[field.key];
71
+ }
72
+ });
73
+
74
+ return validated;
75
+ };