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
@@ -0,0 +1,87 @@
1
+ import app from 'Config/app'; // eslint-disable-line import/no-unresolved
2
+ import { normalizeHttpRequestBeforeHandler } from './normalizeHttpRequestMiddleware';
3
+ import { successHttpResponseHandler } from './successHttpResponseMiddleware';
4
+ import { errorHttpResponseHandler } from './errorHttpResponseMiddleware';
5
+
6
+ const successHttpNoOutputResponseHandler = async opts => {
7
+ const { queryStringParameters } = opts.event;
8
+ const debug =
9
+ queryStringParameters && queryStringParameters.debug && app.debug;
10
+ const response = await successHttpResponseHandler(opts);
11
+
12
+ /* istanbul ignore next */
13
+ if (!debug && !opts.allowResponse(opts)) {
14
+ response.body = null;
15
+ }
16
+
17
+ return response;
18
+ };
19
+
20
+ export const successHttpNoOutputResponseAfterHandler = async (
21
+ handler,
22
+ next,
23
+ opts
24
+ ) => {
25
+ const defaults = {
26
+ response: handler.response,
27
+ event: handler.event,
28
+ allowResponse: () => false,
29
+ };
30
+ const options = { ...defaults, ...opts };
31
+
32
+ // eslint-disable-next-line no-param-reassign
33
+ handler.response = await successHttpNoOutputResponseHandler(options);
34
+
35
+ /* istanbul ignore next */
36
+ next();
37
+ };
38
+
39
+ const errorHttpResponseNoOutputHandler = async opts => {
40
+ const { queryStringParameters } = opts.event;
41
+ const debug =
42
+ queryStringParameters && queryStringParameters.debug && app.debug;
43
+ const response = await errorHttpResponseHandler({
44
+ ...opts,
45
+ debugMode: opts.debugMode || debug,
46
+ });
47
+
48
+ if (!debug && !opts.allowResponse(opts)) {
49
+ response.statusCode = 200;
50
+ response.body = null;
51
+ }
52
+
53
+ return response;
54
+ };
55
+
56
+ export const errorHttpNoOutputResponseAfterHandler = async (
57
+ handler,
58
+ next,
59
+ opts
60
+ ) => {
61
+ const defaults = {
62
+ error: handler.error,
63
+ event: handler.event,
64
+ logger: console.error, // eslint-disable-line no-console
65
+ allowResponse: () => false,
66
+ };
67
+
68
+ const options = { ...defaults, ...opts };
69
+
70
+ // eslint-disable-next-line no-param-reassign
71
+ handler.response = await errorHttpResponseNoOutputHandler(options);
72
+ /* istanbul ignore next */
73
+ next();
74
+ };
75
+
76
+ /* istanbul ignore next */
77
+ const httpNoOutputMiddleware = opts => {
78
+ return {
79
+ before: (handler, next) => normalizeHttpRequestBeforeHandler(handler, next),
80
+ after: (handler, next) =>
81
+ successHttpNoOutputResponseAfterHandler(handler, next, opts),
82
+ onError: (handler, next) =>
83
+ errorHttpNoOutputResponseAfterHandler(handler, next, opts),
84
+ };
85
+ };
86
+
87
+ export default httpNoOutputMiddleware;
@@ -91,7 +91,8 @@ export const normalizeHttpRequestBeforeHandler = (handler, next) => {
91
91
  * Normalizes handler.event.body and handler.event.queryStringParameters
92
92
  * as handler.event.input Object
93
93
  */
94
- const normalizeHttpRequestMiddleware /* istanbul ignore next */ = () => {
94
+ /* istanbul ignore next */
95
+ const normalizeHttpRequestMiddleware = () => {
95
96
  return {
96
97
  before: (handler, next) => normalizeHttpRequestBeforeHandler(handler, next),
97
98
  };
@@ -1,4 +1,7 @@
1
1
  import logger from '../utils/logger';
2
+ import isEmpty from '../utils/isEmpty';
3
+
4
+ const FILE = 'Lesgo/middlewares/normalizeSQSMessageMiddleware';
2
5
 
3
6
  export const normalizeHandler = records => {
4
7
  let recordCount = 0;
@@ -20,11 +23,29 @@ export const normalizeHandler = records => {
20
23
  }));
21
24
  };
22
25
 
26
+ export const disconnectConnections = async opts => {
27
+ try {
28
+ const disconnect = [];
29
+ if (opts) {
30
+ if (!isEmpty(opts.cache)) disconnect.push(opts.cache.end());
31
+ if (!isEmpty(opts.db)) disconnect.push(opts.db.end());
32
+ if (!isEmpty(opts.dbRead)) disconnect.push(opts.dbRead.end());
33
+ }
34
+
35
+ if (disconnect.length > 0) {
36
+ await Promise.all(disconnect);
37
+ }
38
+ } catch (err) {
39
+ logger.error(`${FILE}::Failed to end connection`, err);
40
+ }
41
+ };
42
+
23
43
  /**
24
44
  * Normalizes handler.event.Records as handler.event.collections Object.
25
45
  * This type of request is received by SQS listeners
26
46
  */
27
- const normalizeSQSMessageMiddleware /* istanbul ignore next */ = () => {
47
+ /* istanbul ignore next */
48
+ const normalizeSQSMessageMiddleware /* istanbul ignore next */ = opts => {
28
49
  return {
29
50
  before: (handler, next) => {
30
51
  const { Records } = handler.event;
@@ -32,6 +53,14 @@ const normalizeSQSMessageMiddleware /* istanbul ignore next */ = () => {
32
53
  handler.event.collection = normalizeHandler(Records);
33
54
  next();
34
55
  },
56
+ after: async (handler, next) => {
57
+ await disconnectConnections(opts);
58
+ next();
59
+ },
60
+ onError: async (handler, next) => {
61
+ await disconnectConnections(opts);
62
+ next();
63
+ },
35
64
  };
36
65
  };
37
66
 
@@ -0,0 +1,29 @@
1
+ import { verifyBasicAuthBeforeHandler } from './basicAuthMiddleware';
2
+ import { verifyJwtMiddlewareBeforeHandler } from './verifyJwtMiddleware';
3
+ import { errorHttpResponseAfterHandler } from './errorHttpResponseMiddleware';
4
+
5
+ export const serverAuthBeforeHandler = (handler, next, opts) => {
6
+ try {
7
+ return verifyBasicAuthBeforeHandler(handler, next, opts);
8
+ } catch (e) {
9
+ if (
10
+ e.code !==
11
+ `Middlewares/basicAuthMiddleware::AUTH_INVALID_AUTHORIZATION_TYPE` &&
12
+ e.code !==
13
+ `Middlewares/basicAuthMiddleware::AUTHORIZATION_HEADER_NOT_FOUND`
14
+ )
15
+ throw e;
16
+ }
17
+
18
+ return verifyJwtMiddlewareBeforeHandler(handler, next, opts);
19
+ };
20
+
21
+ /* istanbul ignore next */
22
+ const serverAuthMiddleware = opts => {
23
+ return {
24
+ before: (handler, next) => serverAuthBeforeHandler(handler, next, opts),
25
+ onError: (handler, next) => errorHttpResponseAfterHandler(handler, next),
26
+ };
27
+ };
28
+
29
+ export default serverAuthMiddleware;
@@ -1,5 +1,8 @@
1
1
  import gzipHttpResponse from './gzipHttpResponse';
2
2
  import isEmpty from '../utils/isEmpty';
3
+ import logger from '../utils/logger';
4
+
5
+ const FILE = 'Lesgo/middlewares/successHttpResponseMiddleware';
3
6
 
4
7
  export const successHttpResponseHandler = async opts => {
5
8
  const defaults = {
@@ -25,9 +28,16 @@ export const successHttpResponseHandler = async opts => {
25
28
  const options = { ...defaults, ...optionsHeadersMerged };
26
29
 
27
30
  try {
28
- if (!isEmpty(opts.db)) await opts.db.end();
31
+ const disconnect = [];
32
+ if (!isEmpty(opts.cache)) disconnect.push(opts.cache.end());
33
+ if (!isEmpty(opts.db)) disconnect.push(opts.db.end());
34
+ if (!isEmpty(opts.dbRead)) disconnect.push(opts.dbRead.end());
35
+
36
+ if (disconnect.length > 0) {
37
+ await Promise.all(disconnect);
38
+ }
29
39
  } catch (err) {
30
- // do nothing
40
+ logger.error(`${FILE}::Failed to end connection`, err);
31
41
  }
32
42
 
33
43
  return {
@@ -26,11 +26,16 @@ export const token = headers => {
26
26
  return parsed[1];
27
27
  };
28
28
 
29
- export const verifyJwtMiddlewareBeforeHandler = (handler, next) => {
29
+ export const verifyJwtMiddlewareBeforeHandler = (handler, next, opts) => {
30
30
  const { headers } = handler.event;
31
31
 
32
+ const finalConfig =
33
+ typeof opts !== 'undefined' && 'jwtConfig' in opts
34
+ ? opts.jwtConfig
35
+ : config;
36
+
32
37
  try {
33
- const service = new JwtService(token(headers), config);
38
+ const service = new JwtService(token(headers), finalConfig);
34
39
 
35
40
  // eslint-disable-next-line no-param-reassign
36
41
  handler.event.decodedJwt = service.validate().decoded;
@@ -47,9 +52,10 @@ export const verifyJwtMiddlewareBeforeHandler = (handler, next) => {
47
52
  }
48
53
  };
49
54
 
50
- const verifyJwtMiddleware /* istanbul ignore next */ = () => {
55
+ const verifyJwtMiddleware /* istanbul ignore next */ = opts => {
51
56
  return {
52
- before: (handler, next) => verifyJwtMiddlewareBeforeHandler(handler, next),
57
+ before: (handler, next) =>
58
+ verifyJwtMiddlewareBeforeHandler(handler, next, opts),
53
59
  };
54
60
  };
55
61
 
@@ -13,14 +13,13 @@ const FILE = 'Lesgo/services/AuroraDbRDSProxyService';
13
13
  */
14
14
  export default class AuroraDbRDSProxyService {
15
15
  constructor(opts = {}) {
16
- const { host, user, password, database, persists } = opts;
16
+ const { host, user, password, database } = opts;
17
17
 
18
18
  this.clientOpts = {
19
19
  host,
20
20
  user,
21
21
  password,
22
22
  database,
23
- persists,
24
23
  };
25
24
 
26
25
  this.conn = {};
@@ -48,7 +47,7 @@ export default class AuroraDbRDSProxyService {
48
47
  : this.clientOpts.database,
49
48
  };
50
49
 
51
- const persistentConn = connectionOpts.persists || this.clientOpts.persists;
50
+ const persistentConn = connectionOpts.persists;
52
51
 
53
52
  logger.debug(`${FILE}::PREPARING DB CONNECTION`, {
54
53
  clientOpts,
@@ -1,12 +1,12 @@
1
- import Memcached from 'memcached-elasticache';
1
+ import MemcachePlus from 'memcache-plus';
2
2
 
3
3
  export default class ElastiCacheService {
4
- constructor({ url, options }) {
4
+ constructor(options) {
5
5
  // @TODO:
6
6
  // - determine the driver used 'memcached' or 'redis'
7
7
  // - add switch-case that will set the driver
8
8
 
9
- this.setDriver(new Memcached(url, options));
9
+ this.setDriver(new MemcachePlus(options));
10
10
  }
11
11
 
12
12
  setDriver(driver) {
@@ -28,6 +28,39 @@ describe('ServicesGroup: test ElasticsearchService', () => {
28
28
  });
29
29
  });
30
30
 
31
+ it('test msearch', () => {
32
+ const es = new ElasticsearchService(config.adapters.aws);
33
+ const params = [
34
+ { index: config.adapters.aws.index },
35
+ {
36
+ query: {
37
+ bool: {
38
+ must: { id: 123 },
39
+ must_not: { is_deleted: 1 },
40
+ },
41
+ },
42
+ },
43
+ ];
44
+
45
+ return expect(es.msearch(params)).resolves.toMatchObject({
46
+ mocked: {
47
+ params: {
48
+ body: [
49
+ { index: config.adapters.aws.index },
50
+ {
51
+ query: {
52
+ bool: {
53
+ must: { id: 123 },
54
+ must_not: { is_deleted: 1 },
55
+ },
56
+ },
57
+ },
58
+ ],
59
+ },
60
+ },
61
+ });
62
+ });
63
+
31
64
  it('test get', () => {
32
65
  const es = new ElasticsearchService(config.adapters.aws);
33
66
  return expect(es.get(1)).resolves.toMatchObject({
@@ -1,59 +1,85 @@
1
+ import config from 'Config/cache'; // eslint-disable-line import/no-unresolved
2
+
1
3
  import cache from '../cache';
4
+ import isEmpty from '../isEmpty';
2
5
 
3
- describe('UtilsGroup: test cache utils', () => {
4
- const cacheKey = 'cacheKey';
5
- const cacheTime = 10;
6
- const cacheData = {
7
- someData: [
8
- {
9
- someDataKey1: 'someDataValue1',
10
- },
11
- {
12
- someDataKey2: 'someDataValue2',
13
- },
14
- ],
15
- };
6
+ export const cacheKey = 'cacheKey';
7
+ export const cacheKey2 = 'cacheKey2';
8
+ export const cacheTime = 10;
9
+ export const cacheData = {
10
+ someData: [
11
+ {
12
+ someDataKey1: 'someDataValue1',
13
+ },
14
+ {
15
+ someDataKey2: 'someDataValue2',
16
+ },
17
+ ],
18
+ };
19
+ export const cacheData2 = 'someValue2';
16
20
 
21
+ describe('UtilsGroup: test cache utils', () => {
17
22
  it('test cache utils', async () => {
18
- await expect(cache.set(cacheKey, cacheData, cacheTime)).resolves.toEqual(
19
- true
20
- );
23
+ await expect(cache.set(cacheKey, cacheData, cacheTime)).resolves.toBe();
24
+ await expect(cache.set(cacheKey2, cacheData2, cacheTime)).resolves.toBe();
21
25
 
22
- await expect(cache.get(cacheKey)).resolves.toMatchObject({
23
- data: cacheData,
24
- lifetime: cacheTime,
25
- });
26
+ await expect(cache.get(cacheKey)).resolves.toMatchObject(cacheData);
27
+ await expect(cache.getMulti([cacheKey, cacheKey2])).resolves.toMatchObject([
28
+ cacheData,
29
+ cacheData2,
30
+ ]);
31
+
32
+ await expect(cache.del(cacheKey)).resolves.toBe();
33
+ await expect(cache.delMulti([cacheKey, cacheKey2])).resolves.toBe();
26
34
 
27
- await expect(cache.del(cacheKey)).resolves.toEqual(true);
35
+ await expect(cache.end()).resolves.toBe();
36
+ await expect(cache.disconnect()).resolves.toBe();
28
37
  });
29
38
 
30
- it('test setting cache with error', async () => {
31
- await expect(
32
- cache.set('mockError', cacheData, cacheTime)
33
- ).rejects.toMatchObject({
34
- code: 'CACHE_SET_ERROR',
35
- message: 'mockedError',
36
- name: 'LesgoException',
37
- statusCode: 500,
38
- });
39
+ it('test connect using legacy config', async () => {
40
+ config.connections.memcached = {
41
+ ...config.connections.memcached,
42
+ url: 'localhost',
43
+ };
44
+
45
+ await cache.pConnect('memcached');
46
+ expect(Object.keys(cache.singleton)[0]).toEqual('memcached');
39
47
  });
40
48
 
41
- it('test getting cache with error', async () => {
42
- await expect(cache.get('mockError')).rejects.toMatchObject({
43
- code: 'CACHE_GET_ERROR',
44
- message: 'mockedError',
45
- name: 'LesgoException',
46
- statusCode: 500,
47
- });
49
+ it('test pConnect', async () => {
50
+ await cache.pConnect('memcached');
51
+ expect(Object.keys(cache.singleton)[0]).toEqual('memcached');
48
52
  });
49
53
 
50
- it('test deleting cache with error', async () => {
51
- await expect(cache.del('mockError')).rejects.toMatchObject({
52
- code: 'CACHE_DEL_ERROR',
53
- message: 'mockedError',
54
- name: 'LesgoException',
55
- statusCode: 500,
56
- });
54
+ it('test pConnect without connectionName to default', async () => {
55
+ await cache.pConnect();
56
+ expect(Object.keys(cache.singleton)[0]).toEqual('memcached');
57
+ });
58
+
59
+ it('test disconnecting with pConnect', async () => {
60
+ await cache.pConnect('memcached');
61
+
62
+ await expect(cache.end()).resolves.toBe();
63
+ expect(isEmpty(Object.keys(cache.singleton))).toEqual(true);
64
+ });
65
+
66
+ it('test cache utils with pConnect', async () => {
67
+ await cache.pConnect('memcached');
68
+
69
+ await expect(cache.set(cacheKey, cacheData, cacheTime)).resolves.toBe();
70
+ await expect(cache.set(cacheKey2, cacheData2, cacheTime)).resolves.toBe();
71
+
72
+ await expect(cache.get(cacheKey)).resolves.toMatchObject(cacheData);
73
+ await expect(cache.getMulti([cacheKey, cacheKey2])).resolves.toMatchObject([
74
+ cacheData,
75
+ cacheData2,
76
+ ]);
77
+
78
+ await expect(cache.del(cacheKey)).resolves.toBe();
79
+ await expect(cache.delMulti([cacheKey, cacheKey2])).resolves.toBe();
80
+
81
+ await expect(cache.end()).resolves.toBe();
82
+ await expect(cache.disconnect()).resolves.toBe();
57
83
  });
58
84
 
59
85
  it('test setting cache with exception', async () => {
@@ -76,6 +102,15 @@ describe('UtilsGroup: test cache utils', () => {
76
102
  });
77
103
  });
78
104
 
105
+ it('test getting multiple cache with exception', async () => {
106
+ await expect(cache.getMulti(['mockException'])).rejects.toMatchObject({
107
+ code: 'CACHE_GET_MULTI_EXCEPTION',
108
+ message: 'mockedException',
109
+ name: 'LesgoException',
110
+ statusCode: 500,
111
+ });
112
+ });
113
+
79
114
  it('test deleting cache with exception', async () => {
80
115
  await expect(cache.del('mockException')).rejects.toMatchObject({
81
116
  code: 'CACHE_DEL_EXCEPTION',
@@ -84,4 +119,13 @@ describe('UtilsGroup: test cache utils', () => {
84
119
  statusCode: 500,
85
120
  });
86
121
  });
122
+
123
+ it('test deleting multiple cache with exception', async () => {
124
+ await expect(cache.delMulti(['mockException'])).rejects.toMatchObject({
125
+ code: 'CACHE_DEL_MULTI_EXCEPTION',
126
+ message: 'mockedException',
127
+ name: 'LesgoException',
128
+ statusCode: 500,
129
+ });
130
+ });
87
131
  });
@@ -65,7 +65,6 @@ describe('test db utils instantiate', () => {
65
65
  host: config.connections.rdsProxy.host,
66
66
  user: config.connections.rdsProxy.user,
67
67
  password: config.connections.rdsProxy.password,
68
- persists: config.connections.rdsProxy.persists,
69
68
  });
70
69
  });
71
70
  });