lesgo 0.6.1 → 0.7.1

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.
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ set -Eeuo pipefail
4
+
3
5
  ###############################################################################
4
6
  # #
5
7
  # INITIALIZE EVERYTHING #
6
8
  # #
7
9
  ###############################################################################
8
10
 
9
- usage="$(basename "$0") [-f] [-s] [-t] [-h] [-d] [-y] -- script to deploy serverless functions
11
+ usage="$(basename "$0") [-f] [-s] [-t] [-h] [-d] [-c] [-y] -- script to deploy serverless functions
10
12
 
11
13
  where:
12
14
  -t define the type of action to be taken (build, deploy, invoke, logs, destroy)
@@ -15,22 +17,24 @@ where:
15
17
  -h show this help text
16
18
  -l to invoke a local function
17
19
  -d set data parameters for invoke function
20
+ -c set config as per --config path-to-serverless-yml
18
21
  -y no question/prompt for ci/cd"
19
22
 
20
23
  # arg options
21
- BUILD=0; # serverless build without deploy
22
- DEPLOY=0; # serverless deploy
23
- INVOKE=0; # serverless invoke of specific function
24
- LOGS=0; # serverless stream log of specific function
25
- DESTROY=0; # serverless remove entire service
26
- FUNCTION=''; # specify function to involve
27
- STAGE=''; # deploy specific stage/environment
28
- INVOKE_LOCAL=0; # default to non local execution
29
- DATA='' # set the data parameters for invoke function
30
- NO_QUESTION=0; # default to prompt
24
+ BUILD=0; # serverless build without deploy
25
+ DEPLOY=0; # serverless deploy
26
+ INVOKE=0; # serverless invoke of specific function
27
+ LOGS=0; # serverless stream log of specific function
28
+ DESTROY=0; # serverless remove entire service
29
+ FUNCTION=''; # specify function to involve
30
+ STAGE=''; # deploy specific stage/environment
31
+ INVOKE_LOCAL=0; # default to non local execution
32
+ DATA='' # set the data parameters for invoke function
33
+ NO_QUESTION=0; # default to prompt
34
+ CONFIG='./serverless.yml'; # specify serverless config file to deploy, default to root
31
35
 
32
36
  # parse the options
33
- while getopts "lhs:f:t:d:y" OPT ; do
37
+ while getopts "lhs:f:t:d:c:y" OPT ; do
34
38
  case ${OPT} in
35
39
  f) FUNCTION=${OPTARG} ;;
36
40
  s) STAGE=${OPTARG} ;;
@@ -51,6 +55,7 @@ while getopts "lhs:f:t:d:y" OPT ; do
51
55
  fi;;
52
56
  l) INVOKE_LOCAL=1 ;;
53
57
  d) DATA=${OPTARG} ;;
58
+ c) CONFIG=${OPTARG} ;;
54
59
  y) NO_QUESTION=1 ;;
55
60
  h)
56
61
  echo "${usage}"
@@ -100,8 +105,8 @@ function deploy_func_check ()
100
105
 
101
106
  function deploy_func ()
102
107
  {
103
- echo -e "${YELLOW}Deploying ${FUNCTION} to ${STAGE}${NC}"
104
- sls deploy -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE}
108
+ echo -e "${YELLOW}Deploying ${FUNCTION} to ${STAGE}${NC} using ${CONFIG}"
109
+ sls deploy -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE} --config ${CONFIG}
105
110
  }
106
111
 
107
112
  function prompt_confirmation_deploy_all ()
@@ -110,7 +115,7 @@ function prompt_confirmation_deploy_all ()
110
115
  deploy_full;
111
116
  else
112
117
  while true; do
113
- read -p "Confirm deploy service to ${STAGE} with .env.${ENVFILE}? [Y|N] " yn
118
+ read -p "Confirm deploy service to ${STAGE} with .env.${ENVFILE} using ${CONFIG}? [Y|N] " yn
114
119
  case ${yn} in
115
120
  [Yy] | yes | Yes | YES ) deploy_full; break;;
116
121
  [Nn] | no | No | NO ) echo -e "${YELLOW}Cancelled deploying service to [${STAGE}]${NC}"; exit;;
@@ -123,7 +128,7 @@ function prompt_confirmation_deploy_all ()
123
128
  function prompt_confirmation_deploy_function ()
124
129
  {
125
130
  while true; do
126
- read -p "Confirm deploy function ${FUNCTION} to ${STAGE} with .env.${ENVFILE}? [Y|N] " yn
131
+ read -p "Confirm deploy function ${FUNCTION} to ${STAGE} with .env.${ENVFILE} using ${CONFIG}? [Y|N] " yn
127
132
  case ${yn} in
128
133
  [Yy] | yes | Yes | YES ) deploy_func; break;;
129
134
  [Nn] | no | No | NO ) echo -e "${YELLOW}Cancelled deploying function ${FUNCTION} to [${STAGE}]${NC}"; exit;;
@@ -135,35 +140,35 @@ function prompt_confirmation_deploy_function ()
135
140
  function deploy_full ()
136
141
  {
137
142
  echo -e "${YELLOW}Deploying service to ${STAGE}${NC}"
138
- sls deploy --stage ${STAGE} --env ${ENVFILE}
143
+ sls deploy --stage ${STAGE} --env ${ENVFILE} --config ${CONFIG}
139
144
  }
140
145
 
141
146
  function invoke_func ()
142
147
  {
143
- echo -e "${YELLOW}Invoking function ${FUNCTION} on ${STAGE}${NC}"
148
+ echo -e "${YELLOW}Invoking function ${FUNCTION} on ${STAGE}${NC} using ${CONFIG}"
144
149
  if [ ${INVOKE_LOCAL} == 1 ]; then
145
- sls invoke local -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE} -d ${DATA} -l
150
+ sls invoke local -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE} -d ${DATA} -l --config ${CONFIG}
146
151
  else
147
- sls invoke -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE} -d ${DATA} -l
152
+ sls invoke -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE} -d ${DATA} -l --config ${CONFIG}
148
153
  fi
149
154
  }
150
155
 
151
156
  function log_stream_func ()
152
157
  {
153
- echo -e "${YELLOW}Log Streaming function ${FUNCTION} on ${STAGE}${NC}"
154
- sls logs -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE} -t
158
+ echo -e "${YELLOW}Log Streaming function ${FUNCTION} on ${STAGE}${NC} using ${CONFIG}"
159
+ sls logs -f ${FUNCTION} --stage ${STAGE} --env ${ENVFILE} -t --config ${CONFIG}
155
160
  }
156
161
 
157
162
  function build ()
158
163
  {
159
- echo -e "${YELLOW}Building bundle without deployment${NC}"
160
- sls webpack --stage ${STAGE} --env ${ENVFILE}
164
+ echo -e "${YELLOW}Building bundle without deployment${NC} using ${CONFIG}"
165
+ sls webpack --stage ${STAGE} --env ${ENVFILE} --config ${CONFIG}
161
166
  }
162
167
 
163
168
  function prompt_confirmation_destroy_service ()
164
169
  {
165
170
  while true; do
166
- read -p "Confirm destroy service to ${STAGE} with .env.${ENVFILE}? Note: This action will remove entire service from AWS. Ensure no other applications are using this service. [Y|N] " yn
171
+ read -p "Confirm destroy service to ${STAGE} with .env.${ENVFILE} using ${CONFIG}? Note: This action will remove entire service from AWS. Ensure no other applications are using this service. [Y|N] " yn
167
172
  case ${yn} in
168
173
  [Yy] | yes | Yes | YES ) destroy_service; break;;
169
174
  [Nn] | no | No | NO ) echo -e "${YELLOW}Cancelled destroying service to [${STAGE}]${NC}"; exit;;
@@ -175,7 +180,7 @@ function prompt_confirmation_destroy_service ()
175
180
  function destroy_service ()
176
181
  {
177
182
  echo -e "${YELLOW}Removing service to ${STAGE}${NC}"
178
- sls remove --stage ${STAGE} --env ${ENVFILE}
183
+ sls remove --stage ${STAGE} --env ${ENVFILE} --config ${CONFIG}
179
184
  }
180
185
 
181
186
  ###############################################################################
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lesgo",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Core framework for lesgo node.js serverless framework.",
5
5
  "main": "./src/index.js",
6
6
  "author": "Sufiyan Rahmat",
@@ -57,6 +57,7 @@
57
57
  "firebase-admin": "^9.3.0",
58
58
  "jsonwebtoken": "^8.5.1",
59
59
  "memcached-elasticache": "^1.1.1",
60
+ "mysql2": "^2.2.5",
60
61
  "nanoid": "^3.1.16"
61
62
  },
62
63
  "husky": {
@@ -5,8 +5,8 @@ import {
5
5
  import ValidationErrorException from '../__mocks__/ValidationErrorException';
6
6
 
7
7
  describe('MiddlewareGroup: test errorHandler middleware', () => {
8
- it('test with thrown Error', () => {
9
- const data = errorHttpResponseHandler({
8
+ it('test with thrown Error', async () => {
9
+ const data = await errorHttpResponseHandler({
10
10
  error: new Error('Test validation error'),
11
11
  });
12
12
 
@@ -29,8 +29,8 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
29
29
  expect(dataBody).toHaveProperty('error.details', '');
30
30
  });
31
31
 
32
- it('test with thrown custom Error with default parameters', () => {
33
- const data = errorHttpResponseHandler({
32
+ it('test with thrown custom Error with default parameters', async () => {
33
+ const data = await errorHttpResponseHandler({
34
34
  error: new ValidationErrorException('Test validation error'),
35
35
  });
36
36
  const dataBody = JSON.parse(data.body);
@@ -45,8 +45,8 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
45
45
  expect(dataBody).toHaveProperty('error.details', '');
46
46
  });
47
47
 
48
- it('test with thrown custom Error with given parameters', () => {
49
- const data = errorHttpResponseHandler({
48
+ it('test with thrown custom Error with given parameters', async () => {
49
+ const data = await errorHttpResponseHandler({
50
50
  error: new ValidationErrorException(
51
51
  'Test validation error',
52
52
  'VALIDATION_ERROR_SAMPLE',
@@ -70,8 +70,10 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
70
70
  });
71
71
  });
72
72
 
73
- it('test with error message', () => {
74
- const data = errorHttpResponseHandler({ error: 'Test error message' });
73
+ it('test with error message', async () => {
74
+ const data = await errorHttpResponseHandler({
75
+ error: 'Test error message',
76
+ });
75
77
  const dataBody = JSON.parse(data.body);
76
78
 
77
79
  expect(data.statusCode).toBe(500);
@@ -81,8 +83,8 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
81
83
  expect(dataBody).toHaveProperty('error.details', '');
82
84
  });
83
85
 
84
- it('test with error message with event', () => {
85
- const data = errorHttpResponseHandler({
86
+ it('test with error message with event', async () => {
87
+ const data = await errorHttpResponseHandler({
86
88
  error: 'Test error message',
87
89
  event: {
88
90
  someEventKey: 'someEventValue',
@@ -97,8 +99,8 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
97
99
  });
98
100
  });
99
101
 
100
- it('test with error message with event in non-debug mode', () => {
101
- const data = errorHttpResponseHandler({
102
+ it('test with error message with event in non-debug mode', async () => {
103
+ const data = await errorHttpResponseHandler({
102
104
  error: 'Test error message',
103
105
  event: {
104
106
  someEventKey: 'someEventValue',
@@ -110,8 +112,8 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
110
112
  expect(dataBody).toHaveProperty('_meta', {});
111
113
  });
112
114
 
113
- it('test with undefined opts', () => {
114
- const data = errorHttpResponseHandler();
115
+ it('test with undefined opts', async () => {
116
+ const data = await errorHttpResponseHandler();
115
117
 
116
118
  expect(data.headers['Access-Control-Allow-Origin']).toBe('*');
117
119
  expect(data.headers['Cache-Control']).toBe('no-cache');
@@ -128,16 +130,31 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
128
130
  expect(dataBody).toHaveProperty('error.message', '');
129
131
  expect(dataBody).toHaveProperty('error.details', '');
130
132
  });
133
+
134
+ it('should call db.end() whenever a db options is set', async () => {
135
+ const end = jest.fn().mockResolvedValue();
136
+ await errorHttpResponseHandler({
137
+ error: 'Test error message',
138
+ event: {
139
+ someEventKey: 'someEventValue',
140
+ },
141
+ db: {
142
+ end,
143
+ },
144
+ });
145
+
146
+ expect(end).toHaveBeenCalledTimes(1);
147
+ });
131
148
  });
132
149
 
133
150
  describe('MiddlewareGroup: test errorHttpResponseAfterHandler', () => {
134
- it('test with default parameters', () => {
151
+ it('test with default parameters', async () => {
135
152
  const handler = {
136
153
  error: {},
137
154
  event: {},
138
155
  };
139
156
 
140
- errorHttpResponseAfterHandler(handler, () => {});
157
+ await errorHttpResponseAfterHandler(handler, () => {});
141
158
  expect(handler.response).toHaveProperty('statusCode', 500);
142
159
  });
143
160
  });
@@ -1,7 +1,9 @@
1
+ import app from 'Config/app'; // eslint-disable-line import/no-unresolved
1
2
  import {
2
3
  normalizeRequest,
3
4
  normalizeHttpRequestBeforeHandler,
4
5
  } from '../normalizeHttpRequestMiddleware';
6
+ import logger from '../../utils/logger';
5
7
 
6
8
  describe('MiddlewareGroup: test normalizeRequest', () => {
7
9
  it('test with default parameters', () => {
@@ -116,4 +118,26 @@ describe('MiddlewareGroup: test normalizeHttpRequestBeforeHandler', () => {
116
118
  normalizeHttpRequestBeforeHandler(handler, () => {});
117
119
  expect(handler.event.auth.sub).toBe('f2b5349d-f5e3-44f5-9c08-ae6b01e95434');
118
120
  });
121
+
122
+ it('should not set meta on debug', () => {
123
+ app.debug = true;
124
+ const handler = {
125
+ event: {
126
+ headers: {},
127
+ auth: '1',
128
+ queryStringParameters: {
129
+ foo: 'bar',
130
+ },
131
+ body: {
132
+ test: 'body',
133
+ },
134
+ },
135
+ };
136
+ normalizeHttpRequestBeforeHandler(handler, () => {});
137
+ expect(logger.meta.auth).toBe(handler.event.auth);
138
+ expect(logger.meta.queryStringParameters).toStrictEqual(
139
+ handler.event.queryStringParameters
140
+ );
141
+ expect(logger.meta.body).toStrictEqual(handler.event.body);
142
+ });
119
143
  });
@@ -4,8 +4,8 @@ import {
4
4
  } from '../successHttpResponseMiddleware';
5
5
 
6
6
  describe('MiddlewareGroup: test successHttpResponseHandler middleware', () => {
7
- it('test default without parameters', () => {
8
- const data = successHttpResponseHandler();
7
+ it('test default without parameters', async () => {
8
+ const data = await successHttpResponseHandler();
9
9
 
10
10
  expect(data.headers['Access-Control-Allow-Origin']).toBe('*');
11
11
  expect(data.headers['Cache-Control']).toBe('no-cache');
@@ -20,8 +20,8 @@ describe('MiddlewareGroup: test successHttpResponseHandler middleware', () => {
20
20
  expect(dataBody).toHaveProperty('_meta', {});
21
21
  });
22
22
 
23
- it('test default', () => {
24
- const data = successHttpResponseHandler({ response: 'Some message' });
23
+ it('test default', async () => {
24
+ const data = await successHttpResponseHandler({ response: 'Some message' });
25
25
 
26
26
  expect(data.headers['Access-Control-Allow-Origin']).toBe('*');
27
27
  expect(data.headers['Cache-Control']).toBe('no-cache');
@@ -36,8 +36,8 @@ describe('MiddlewareGroup: test successHttpResponseHandler middleware', () => {
36
36
  expect(dataBody).toHaveProperty('_meta', {});
37
37
  });
38
38
 
39
- it('test with status code and event', () => {
40
- const data = successHttpResponseHandler({
39
+ it('test with status code and event', async () => {
40
+ const data = await successHttpResponseHandler({
41
41
  response: 'Some message',
42
42
  statusCode: 201,
43
43
  event: {
@@ -53,8 +53,8 @@ describe('MiddlewareGroup: test successHttpResponseHandler middleware', () => {
53
53
  expect(dataBody).toHaveProperty('_meta', {});
54
54
  });
55
55
 
56
- it('test with status code and event in debug mode', () => {
57
- const data = successHttpResponseHandler({
56
+ it('test with status code and event in debug mode', async () => {
57
+ const data = await successHttpResponseHandler({
58
58
  response: 'Some message',
59
59
  statusCode: 201,
60
60
  event: {
@@ -73,8 +73,8 @@ describe('MiddlewareGroup: test successHttpResponseHandler middleware', () => {
73
73
  });
74
74
  });
75
75
 
76
- it('test with configurable header', () => {
77
- const data = successHttpResponseHandler({
76
+ it('test with configurable header', async () => {
77
+ const data = await successHttpResponseHandler({
78
78
  response: 'Some message',
79
79
  headers: {
80
80
  'Access-Control-Allow-Credentials': false,
@@ -96,16 +96,32 @@ describe('MiddlewareGroup: test successHttpResponseHandler middleware', () => {
96
96
  expect(dataBody).toHaveProperty('data', 'Some message');
97
97
  expect(dataBody).toHaveProperty('_meta', {});
98
98
  });
99
+
100
+ it('should call db.end() whenever a db options is set', async () => {
101
+ const end = jest.fn().mockResolvedValue();
102
+ await successHttpResponseHandler({
103
+ response: 'Some message',
104
+ headers: {
105
+ 'Access-Control-Allow-Credentials': false,
106
+ 'X-Token-Id': 'token',
107
+ },
108
+ db: {
109
+ end,
110
+ },
111
+ });
112
+
113
+ expect(end).toHaveBeenCalledTimes(1);
114
+ });
99
115
  });
100
116
 
101
117
  describe('MiddlewareGroup: test successHttpResponseAfterHandler', () => {
102
- it('test with default parameters', () => {
118
+ it('test with default parameters', async () => {
103
119
  const handler = {
104
120
  response: {},
105
121
  event: {},
106
122
  };
107
123
 
108
- successHttpResponseAfterHandler(handler, () => {});
124
+ await successHttpResponseAfterHandler(handler, () => {});
109
125
  expect(handler.response).toHaveProperty('statusCode', 200);
110
126
  });
111
127
  });
@@ -1,6 +1,7 @@
1
1
  import logger from '../utils/logger';
2
+ import isEmpty from '../utils/isEmpty';
2
3
 
3
- export const errorHttpResponseHandler = opts => {
4
+ export const errorHttpResponseHandler = async opts => {
4
5
  const defaults = {
5
6
  response: '',
6
7
  statusCode: 500,
@@ -47,6 +48,12 @@ export const errorHttpResponseHandler = opts => {
47
48
  logger.warn(options.error);
48
49
  }
49
50
 
51
+ try {
52
+ if (!isEmpty(opts.db)) await opts.db.end();
53
+ } catch (err) {
54
+ // do nothing
55
+ }
56
+
50
57
  return {
51
58
  headers: options.headers,
52
59
  statusCode,
@@ -54,7 +61,7 @@ export const errorHttpResponseHandler = opts => {
54
61
  };
55
62
  };
56
63
 
57
- export const errorHttpResponseAfterHandler = (handler, next, opts) => {
64
+ export const errorHttpResponseAfterHandler = async (handler, next, opts) => {
58
65
  const defaults = {
59
66
  error: handler.error,
60
67
  event: handler.event,
@@ -64,7 +71,7 @@ export const errorHttpResponseAfterHandler = (handler, next, opts) => {
64
71
  const options = { ...defaults, ...opts };
65
72
 
66
73
  // eslint-disable-next-line no-param-reassign
67
- handler.response = errorHttpResponseHandler(options);
74
+ handler.response = await errorHttpResponseHandler(options);
68
75
  /* istanbul ignore next */
69
76
  next();
70
77
  };
@@ -31,7 +31,7 @@ export const gzip = async response => {
31
31
  * Determine request origin
32
32
  */
33
33
  export const determineRequestOrigin = handler => {
34
- const { requestContext } = handler.event;
34
+ const { requestContext = {} } = handler.event;
35
35
  let requestFrom = 'APIGATEWAY';
36
36
 
37
37
  if (requestContext.elb) {
@@ -42,9 +42,12 @@ export const normalizeHttpRequestBeforeHandler = (handler, next) => {
42
42
  // eslint-disable-next-line no-param-reassign
43
43
  handler.event.input = normalizeRequest(options);
44
44
 
45
+ const authHeader =
46
+ options.headers.Authorization || options.headers.authorization;
47
+
45
48
  const auth = {};
46
- if (handler.event.headers.Authorization) {
47
- auth.sub = getJwtSubFromAuthHeader(handler.event.headers.Authorization);
49
+ if (authHeader) {
50
+ auth.sub = getJwtSubFromAuthHeader(authHeader);
48
51
  }
49
52
 
50
53
  // eslint-disable-next-line no-param-reassign
@@ -1,6 +1,7 @@
1
1
  import gzipHttpResponse from './gzipHttpResponse';
2
+ import isEmpty from '../utils/isEmpty';
2
3
 
3
- export const successHttpResponseHandler = opts => {
4
+ export const successHttpResponseHandler = async opts => {
4
5
  const defaults = {
5
6
  response: '',
6
7
  statusCode: 200,
@@ -23,6 +24,12 @@ export const successHttpResponseHandler = opts => {
23
24
 
24
25
  const options = { ...defaults, ...optionsHeadersMerged };
25
26
 
27
+ try {
28
+ if (!isEmpty(opts.db)) await opts.db.end();
29
+ } catch (err) {
30
+ // do nothing
31
+ }
32
+
26
33
  return {
27
34
  headers: options.headers,
28
35
  statusCode: options.statusCode,
@@ -43,7 +50,7 @@ export const successHttpResponseAfterHandler = async (handler, next, opts) => {
43
50
  const options = { ...defaults, ...opts };
44
51
 
45
52
  // eslint-disable-next-line no-param-reassign
46
- handler.response = successHttpResponseHandler(options);
53
+ handler.response = await successHttpResponseHandler(options);
47
54
 
48
55
  // eslint-disable-next-line no-param-reassign
49
56
  handler.response = await gzipHttpResponse(handler, opts);
@@ -3,7 +3,9 @@ import JwtService from '../services/JwtService';
3
3
  import LesgoException from '../exceptions/LesgoException';
4
4
 
5
5
  export const token = headers => {
6
- if (!headers.Authorization) {
6
+ const authorizationHeader = headers.Authorization || headers.authorization;
7
+
8
+ if (!authorizationHeader) {
7
9
  throw new LesgoException(
8
10
  'Authorization Header is required!',
9
11
  'JWT_MISSING_AUTHORIZATION_HEADER',
@@ -11,7 +13,7 @@ export const token = headers => {
11
13
  );
12
14
  }
13
15
 
14
- const parsed = headers.Authorization.split(' ');
16
+ const parsed = authorizationHeader.split(' ');
15
17
 
16
18
  if (!parsed[1]) {
17
19
  throw new LesgoException(