lesgo 0.7.6 → 0.7.8

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 (27) hide show
  1. package/README.md +1 -1
  2. package/package.json +23 -23
  3. package/src/middlewares/__tests__/basicAuthMiddleware.spec.js +225 -0
  4. package/src/middlewares/__tests__/clientAuthMiddleware.spec.js +137 -0
  5. package/src/middlewares/__tests__/errorHttpResponseMiddleware.spec.js +30 -0
  6. package/src/middlewares/__tests__/httpNoOutputMiddleware.spec.js +199 -0
  7. package/src/middlewares/__tests__/normalizeSQSMessageMiddleware.spec.js +89 -1
  8. package/src/middlewares/__tests__/serverAuthMiddleware.spec.js +170 -0
  9. package/src/middlewares/__tests__/successHttpResponseMiddleware.spec.js +32 -0
  10. package/src/middlewares/__tests__/verifyJwtMiddleware.spec.js +49 -1
  11. package/src/middlewares/basicAuthMiddleware.js +145 -0
  12. package/src/middlewares/clientAuthMiddleware.js +82 -0
  13. package/src/middlewares/errorHttpResponseMiddleware.js +9 -2
  14. package/src/middlewares/httpNoOutputMiddleware.js +87 -0
  15. package/src/middlewares/normalizeHttpRequestMiddleware.js +2 -1
  16. package/src/middlewares/normalizeSQSMessageMiddleware.js +30 -1
  17. package/src/middlewares/serverAuthMiddleware.js +29 -0
  18. package/src/middlewares/successHttpResponseMiddleware.js +12 -2
  19. package/src/middlewares/verifyJwtMiddleware.js +10 -4
  20. package/src/services/AuroraDbRDSProxyService.js +2 -3
  21. package/src/services/ElastiCacheService.js +3 -3
  22. package/src/services/__tests__/ElasticsearchService.spec.js +33 -0
  23. package/src/utils/__tests__/cache.spec.js +88 -44
  24. package/src/utils/__tests__/db.spec.js +0 -1
  25. package/src/utils/__tests__/validateFields.spec.js +223 -32
  26. package/src/utils/cache.js +157 -51
  27. package/src/utils/validateFields.js +85 -44
@@ -12,6 +12,13 @@ const params = {
12
12
  status: 'active',
13
13
  decimalCheck: 1.99,
14
14
  totalRecord: 99,
15
+ functionCheck: () => {},
16
+ jsonCheck: JSON.stringify({
17
+ test: 'json value',
18
+ testArray: ['withvalues', 'here'],
19
+ testNum: 1,
20
+ }),
21
+ statusCollection: ['active', 'inactive', 'active'],
15
22
  };
16
23
 
17
24
  const validFields = [
@@ -26,8 +33,29 @@ const validFields = [
26
33
  enumValues: ['active', 'inactive'],
27
34
  required: true,
28
35
  },
29
- { key: 'decimalCheck', type: 'decimal', required: true },
36
+ {
37
+ key: 'decimalCheck',
38
+ type: 'decimal',
39
+ required: true,
40
+ },
30
41
  { key: 'totalRecord', type: 'number', required: true },
42
+ {
43
+ key: 'functionCheck',
44
+ type: 'function',
45
+ required: true,
46
+ },
47
+ {
48
+ key: 'jsonCheck',
49
+ type: 'json',
50
+ required: true,
51
+ },
52
+ {
53
+ key: 'statusCollection',
54
+ type: 'enum',
55
+ enumValues: ['active', 'inactive'],
56
+ required: true,
57
+ isCollection: true,
58
+ },
31
59
  ];
32
60
 
33
61
  describe('test Utils/validateFields', () => {
@@ -39,56 +67,92 @@ describe('test Utils/validateFields', () => {
39
67
  const newParams = { ...params };
40
68
  delete newParams.email;
41
69
 
42
- expect(() => validateFields(newParams, validFields)).toThrow(
43
- new LesgoException(
44
- "Missing required 'email'",
45
- `${FILE}::MISSING_REQUIRED_EMAIL}`
46
- )
47
- );
70
+ try {
71
+ const validated = validateFields(newParams, validFields);
72
+
73
+ expect(validated).toThrow();
74
+ } catch (e) {
75
+ expect(e.name).toEqual('LesgoException');
76
+ expect(e.message).toEqual("Missing required 'email'");
77
+ expect(e.code).toEqual(`${FILE}::MISSING_REQUIRED_EMAIL`);
78
+ expect(e.extra).toStrictEqual({
79
+ field: validFields[2],
80
+ });
81
+ }
48
82
  });
49
83
 
50
84
  it('should throw invalid type when non-string value check', () => {
51
85
  const newParams = { ...params, name: 123 };
52
86
 
53
- expect(() => validateFields(newParams, validFields)).toThrow(
54
- new LesgoException(
55
- `Invalid type for 'name', expecting 'string'`,
56
- `${FILE}::INVALID_TYPE_NAME`
57
- )
58
- );
87
+ try {
88
+ const validated = validateFields(newParams, validFields);
89
+
90
+ expect(validated).toThrow();
91
+ } catch (e) {
92
+ expect(e.name).toEqual('LesgoException');
93
+ expect(e.message).toEqual(`Invalid type for 'name', expecting 'string'`);
94
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_NAME`);
95
+ expect(e.extra).toStrictEqual({
96
+ field: validFields[1],
97
+ value: 123,
98
+ });
99
+ }
59
100
  });
60
101
 
61
102
  it('should throw invalid type when non-object value check', () => {
62
103
  const newParams = { ...params, roles: 1597929335 };
63
104
 
64
- expect(() => validateFields(newParams, validFields)).toThrow(
65
- new LesgoException(
66
- `Invalid type for 'roles', expecting 'object'`,
67
- `${FILE}::INVALID_TYPE_ROLES`
68
- )
69
- );
105
+ try {
106
+ const validated = validateFields(newParams, validFields);
107
+
108
+ expect(validated).toThrow();
109
+ } catch (e) {
110
+ expect(e.name).toEqual('LesgoException');
111
+ expect(e.message).toEqual(`Invalid type for 'roles', expecting 'object'`);
112
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_ROLES`);
113
+ expect(e.extra).toStrictEqual({
114
+ field: validFields[3],
115
+ value: 1597929335,
116
+ });
117
+ }
70
118
  });
71
119
 
72
120
  it('should throw invalid type when non-number value check', () => {
73
121
  const newParams = { ...params, Id: '123' };
74
122
 
75
- expect(() => validateFields(newParams, validFields)).toThrow(
76
- new LesgoException(
77
- `Invalid type for 'Id', expecting 'number'`,
78
- `${FILE}::INVALID_TYPE_ID`
79
- )
80
- );
123
+ try {
124
+ const validated = validateFields(newParams, validFields);
125
+
126
+ expect(validated).toThrow();
127
+ } catch (e) {
128
+ expect(e.name).toEqual('LesgoException');
129
+ expect(e.message).toEqual(`Invalid type for 'Id', expecting 'number'`);
130
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_ID`);
131
+ expect(e.extra).toStrictEqual({
132
+ field: validFields[0],
133
+ value: '123',
134
+ });
135
+ }
81
136
  });
82
137
 
83
138
  it('should throw invalid type when non-array value check', () => {
84
139
  const newParams = { ...params, listItem: { created_at: 1597929335 } };
85
140
 
86
- expect(() => validateFields(newParams, validFields)).toThrow(
87
- new LesgoException(
88
- `Invalid type for 'listItem', expecting 'array'`,
89
- `${FILE}::INVALID_TYPE_LISTITEM`
90
- )
91
- );
141
+ try {
142
+ const validated = validateFields(newParams, validFields);
143
+
144
+ expect(validated).toThrow();
145
+ } catch (e) {
146
+ expect(e.name).toEqual('LesgoException');
147
+ expect(e.message).toEqual(
148
+ `Invalid type for 'listItem', expecting 'array'`
149
+ );
150
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_LISTITEM`);
151
+ expect(e.extra).toStrictEqual({
152
+ field: validFields[4],
153
+ value: { created_at: 1597929335 },
154
+ });
155
+ }
92
156
  });
93
157
 
94
158
  it('should throw required when array value is empty but required', () => {
@@ -119,9 +183,136 @@ describe('test Utils/validateFields', () => {
119
183
  expect(() => validateFields(newParams, validFields)).toThrow(
120
184
  new LesgoException(
121
185
  `Invalid type for 'status', expecting 'enum'`,
122
- `${FILE}::INVALID_TYPE_STATUS`
186
+ `Invalid type for 'status', expecting 'enum'`
123
187
  )
124
188
  );
189
+
190
+ try {
191
+ const validated = validateFields(newParams, validFields);
192
+
193
+ expect(validated).toThrow();
194
+ } catch (e) {
195
+ expect(e.name).toEqual('LesgoException');
196
+ expect(e.message).toEqual(`Invalid type for 'status', expecting 'enum'`);
197
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_STATUS`);
198
+ expect(e.extra).toStrictEqual({
199
+ field: validFields[5],
200
+ value: 'private',
201
+ });
202
+ }
203
+ });
204
+
205
+ it('should throw invalid type when non-function value check', async () => {
206
+ const newParams = {
207
+ ...params,
208
+ functionCheck: { not: 'function' },
209
+ };
210
+
211
+ try {
212
+ const validated = validateFields(newParams, validFields);
213
+
214
+ expect(validated).toThrow();
215
+ } catch (e) {
216
+ expect(e.name).toEqual('LesgoException');
217
+ expect(e.message).toEqual(
218
+ `Invalid type for 'functionCheck', expecting 'function'`
219
+ );
220
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_FUNCTIONCHECK`);
221
+ expect(e.extra).toStrictEqual({
222
+ field: validFields[8],
223
+ value: { not: 'function' },
224
+ });
225
+ }
226
+ });
227
+
228
+ it.each`
229
+ value
230
+ ${'not a json'}
231
+ ${3}
232
+ ${'"invalid'}
233
+ ${{ not: 'json' }}
234
+ ${'{"missing":"bracket"}}'}
235
+ `(
236
+ 'should throw invalid type when non-json $value check',
237
+ async ({ value }) => {
238
+ const newParams = {
239
+ ...params,
240
+ jsonCheck: value,
241
+ };
242
+
243
+ try {
244
+ const validated = validateFields(newParams, validFields);
245
+
246
+ expect(validated).toThrow();
247
+ } catch (e) {
248
+ expect(e.name).toEqual('LesgoException');
249
+ expect(e.message).toEqual(
250
+ `Invalid type for 'jsonCheck', expecting 'json'`
251
+ );
252
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_JSONCHECK`);
253
+ expect(e.extra).toStrictEqual({
254
+ field: validFields[9],
255
+ value,
256
+ });
257
+ }
258
+ }
259
+ );
260
+
261
+ it('should throw invalid type when non-collection value check', async () => {
262
+ const newParams = {
263
+ ...params,
264
+ statusCollection: 'active',
265
+ };
266
+
267
+ try {
268
+ const validated = validateFields(newParams, validFields);
269
+
270
+ expect(validated).toThrow();
271
+ } catch (e) {
272
+ expect(e.name).toEqual('LesgoException');
273
+ expect(e.message).toEqual(
274
+ `Invalid type for 'statusCollection', expecting collection of 'enum'`
275
+ );
276
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_STATUSCOLLECTION`);
277
+ expect(e.extra).toStrictEqual({
278
+ field: validFields[10],
279
+ value: 'active',
280
+ });
281
+ }
282
+ });
283
+
284
+ it('should ignore type when collection value check is non-required', async () => {
285
+ const newParams = { ...params };
286
+ const newValidFields = [...validFields];
287
+ delete newParams.statusCollection;
288
+ newValidFields[10].required = false;
289
+
290
+ const validated = validateFields(newParams, newValidFields);
291
+
292
+ expect(validated).toStrictEqual(newParams);
293
+ });
294
+
295
+ it('should throw invalid type when non-enum collection value check', async () => {
296
+ const newParams = {
297
+ ...params,
298
+ statusCollection: ['archive'],
299
+ };
300
+
301
+ try {
302
+ const validated = validateFields(newParams, validFields);
303
+
304
+ expect(validated).toThrow();
305
+ } catch (e) {
306
+ expect(e.name).toEqual('LesgoException');
307
+ expect(e.message).toEqual(
308
+ `Invalid type for 'statusCollection', expecting collection of 'enum'`
309
+ );
310
+ expect(e.code).toEqual(`${FILE}::INVALID_TYPE_STATUSCOLLECTION`);
311
+ expect(e.extra).toStrictEqual({
312
+ field: validFields[10],
313
+ value: 'archive',
314
+ });
315
+ }
125
316
  });
126
317
 
127
318
  it('should return only valid and allowed fields when other fields are received', () => {
@@ -1,54 +1,104 @@
1
1
  import config from 'Config/cache'; // eslint-disable-line import/no-unresolved
2
2
  import ElastiCacheService from '../services/ElastiCacheService';
3
3
  import LesgoException from '../exceptions/LesgoException';
4
+ import logger from './logger';
5
+ import isEmpty from './isEmpty';
6
+
7
+ const FILE = 'Lesgo/utils/cache';
4
8
 
5
9
  /**
6
10
  * Reusable instance
7
11
  */
8
- const singleton = [];
12
+ const singleton = {};
9
13
 
10
14
  /**
11
15
  * Instantiate the Cache Service
12
16
  *
13
- * @param {object} conn The connection options
17
+ * @param {String} connectionName The name of the driver config to connect to
18
+ * @param {boolean} persists To use persistent connection or otherwise
19
+ *
14
20
  * @return {object} Returns the driver
15
21
  */
16
- const ec = (conn = null) => {
22
+ const ec = (connectionName = '', persists = false) => {
23
+ const conn = connectionName || config.default;
17
24
  if (singleton[conn]) {
18
25
  return singleton[conn];
19
26
  }
20
27
 
21
- const { driver } = new ElastiCacheService({
22
- ...config.connections[conn || config.default],
23
- });
28
+ let { options } = config.connections[conn];
29
+
30
+ if (config.connections[conn].url) {
31
+ // this is a legacy config with memcached-elasticache package
32
+ // remap to new config
33
+ options = {
34
+ ...options,
35
+ hosts: [config.connections[conn].url],
36
+ };
37
+ }
38
+
39
+ const { driver } = new ElastiCacheService(options);
24
40
 
25
- singleton[conn] = driver;
41
+ if (persists) {
42
+ singleton[conn] = driver;
43
+ }
26
44
 
27
45
  return driver;
28
46
  };
29
47
 
48
+ /**
49
+ * Persistent connection if declared in the handler
50
+ */
51
+ const pConnect = (connectionName = '') => {
52
+ return ec(connectionName, true);
53
+ };
54
+
30
55
  /**
31
56
  * Fetch data from cache by key
32
57
  *
33
58
  * @param {string} key Cache key to fetch data.
34
59
  * @return {promise} Returns promised.
35
60
  */
36
- const get = key => {
37
- return new Promise((res, rej) => {
38
- try {
39
- ec().get(key, (err, data) => {
40
- if (err) {
41
- rej(new LesgoException(err, 'CACHE_GET_ERROR'));
42
- } else {
43
- res(data);
44
- }
45
-
46
- ec().end();
47
- });
48
- } catch (err) {
49
- rej(new LesgoException(err.message, 'CACHE_GET_EXCEPTION', 500, err));
61
+ const get = async key => {
62
+ try {
63
+ const client = ec();
64
+ const data = await client.get(key);
65
+ logger.debug(`${FILE}::Fetched cache data`, { key, data });
66
+ if (isEmpty(singleton)) {
67
+ logger.debug(`${FILE}::Ending cache connection`);
68
+ await client.disconnect();
69
+ logger.debug(`${FILE}::Ended cache connection`);
50
70
  }
51
- });
71
+ return data;
72
+ } catch (err) {
73
+ throw new LesgoException(err.message, 'CACHE_GET_EXCEPTION', 500, err);
74
+ }
75
+ };
76
+
77
+ /**
78
+ * Fetch data from cache by multiple keys
79
+ *
80
+ * @param {array} keys Array of Cache keys to fetch data.
81
+ * @return {promise} Returns promised.
82
+ */
83
+ const getMulti = async keys => {
84
+ try {
85
+ const client = ec();
86
+ const data = await client.getMulti(keys);
87
+ logger.debug(`${FILE}::Fetched cache data`, { keys, data });
88
+ if (isEmpty(singleton)) {
89
+ logger.debug(`${FILE}::Ending cache connection`);
90
+ await client.disconnect();
91
+ logger.debug(`${FILE}::Ended cache connection`);
92
+ }
93
+ return data;
94
+ } catch (err) {
95
+ throw new LesgoException(
96
+ err.message,
97
+ 'CACHE_GET_MULTI_EXCEPTION',
98
+ 500,
99
+ err
100
+ );
101
+ }
52
102
  };
53
103
 
54
104
  /**
@@ -59,22 +109,19 @@ const get = key => {
59
109
  * @param {integer} lifetime Time in seconds to expire cache.
60
110
  * @return {promise} Returns promised.
61
111
  */
62
- const set = (key, val, lifetime) => {
63
- return new Promise((res, rej) => {
64
- try {
65
- ec().set(key, val, lifetime, err => {
66
- if (err) {
67
- rej(new LesgoException(err, 'CACHE_SET_ERROR'));
68
- } else {
69
- res(true);
70
- }
71
-
72
- ec().end();
73
- });
74
- } catch (err) {
75
- rej(new LesgoException(err.message, 'CACHE_SET_EXCEPTION', 500, err));
112
+ const set = async (key, val, lifetime) => {
113
+ try {
114
+ const client = ec();
115
+ await client.set(key, val, lifetime);
116
+ logger.debug(`${FILE}::Cache stored`, { key, val, lifetime });
117
+ if (isEmpty(singleton)) {
118
+ logger.debug(`${FILE}::Ending cache connection`);
119
+ await client.disconnect();
120
+ logger.debug(`${FILE}::Ended cache connection`);
76
121
  }
77
- });
122
+ } catch (err) {
123
+ throw new LesgoException(err.message, 'CACHE_SET_EXCEPTION', 500, err);
124
+ }
78
125
  };
79
126
 
80
127
  /**
@@ -83,27 +130,86 @@ const set = (key, val, lifetime) => {
83
130
  * @param {string} key Cache key to delete.
84
131
  * @return {promise} Returns promised.
85
132
  */
86
- const del = key => {
87
- return new Promise((res, rej) => {
88
- try {
89
- ec().del(key, err => {
90
- if (err) {
91
- rej(new LesgoException(err, 'CACHE_DEL_ERROR'));
92
- } else {
93
- res(true);
94
- }
95
-
96
- ec().end();
133
+ const del = async key => {
134
+ try {
135
+ const client = ec();
136
+ await client.delete(key);
137
+ logger.debug(`${FILE}::Key deleted from cache`, { key });
138
+ if (isEmpty(singleton)) {
139
+ logger.debug(`${FILE}::Ending cache connection`);
140
+ await client.disconnect();
141
+ logger.debug(`${FILE}::Ended cache connection`);
142
+ }
143
+ } catch (err) {
144
+ throw new LesgoException(err.message, 'CACHE_DEL_EXCEPTION', 500, err);
145
+ }
146
+ };
147
+
148
+ /**
149
+ * Remove data from cache by multiple keys
150
+ *
151
+ * @param {array} keys Array of Cache keys to delete data.
152
+ * @return {promise} Returns promised.
153
+ */
154
+ const delMulti = async keys => {
155
+ try {
156
+ const client = ec();
157
+ await client.deleteMulti(keys);
158
+ logger.debug(`${FILE}::Keys deleted from cache`, { keys });
159
+ if (isEmpty(singleton)) {
160
+ logger.debug(`${FILE}::Ending cache connection`);
161
+ await client.disconnect();
162
+ logger.debug(`${FILE}::Ended cache connection`);
163
+ }
164
+ } catch (err) {
165
+ throw new LesgoException(
166
+ err.message,
167
+ 'CACHE_DEL_MULTI_EXCEPTION',
168
+ 500,
169
+ err
170
+ );
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Ends the connection
176
+ *
177
+ * @return {promise} Returns promised.
178
+ */
179
+ const end = async () => {
180
+ try {
181
+ logger.debug(`${FILE}::Ending cache connection`);
182
+ await ec().disconnect();
183
+ logger.debug(`${FILE}::Ended cache connection`);
184
+
185
+ if (!isEmpty(Object.keys(singleton))) {
186
+ Object.keys(singleton).forEach(key => {
187
+ delete singleton[key];
97
188
  });
98
- } catch (err) {
99
- rej(new LesgoException(err.message, 'CACHE_DEL_EXCEPTION', 500, err));
100
189
  }
101
- });
190
+ } catch (err) {
191
+ throw new LesgoException(err.message, 'CACHE_END_EXCEPTION', 500, err);
192
+ }
193
+ };
194
+
195
+ /**
196
+ * Alias of end()
197
+ *
198
+ * @return {promise} Returns promised.
199
+ */
200
+ const disconnect = () => {
201
+ return end();
102
202
  };
103
203
 
104
204
  export default {
205
+ pConnect,
105
206
  ec,
106
207
  get,
208
+ getMulti,
107
209
  set,
108
210
  del,
211
+ delMulti,
212
+ end,
213
+ disconnect,
214
+ singleton,
109
215
  };