lesgo 0.6.3 → 0.7.3

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.3",
3
+ "version": "0.7.3",
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) {
@@ -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);
@@ -0,0 +1,183 @@
1
+ import mysql from 'mysql2/promise';
2
+ import logger from '../utils/logger';
3
+ import isEmpty from '../utils/isEmpty';
4
+ import LesgoException from '../exceptions/LesgoException';
5
+ import LengthAwarePaginator from './pagination/LengthAwarePaginator';
6
+ import Paginator from './pagination/Paginator';
7
+
8
+ const FILE = 'Lesgo/services/AuroraDbRDSProxyService';
9
+
10
+ /**
11
+ * Service to connect to AWS Aurora Provisioned MySQL Database via RDS Proxy.
12
+ * Use Services/AuroraDbServerlessService for the Serverless type via Data API.
13
+ */
14
+ export default class AuroraDbRDSProxyService {
15
+ constructor(opts = {}) {
16
+ const { host, user, password, database, persists } = opts;
17
+
18
+ this.clientOpts = {
19
+ host,
20
+ user,
21
+ password,
22
+ database,
23
+ persists,
24
+ };
25
+
26
+ this.conn = {};
27
+ }
28
+
29
+ /**
30
+ * Persists the database connection for later use
31
+ *
32
+ * @param {*} connectionOpts
33
+ * @returns
34
+ */
35
+ async pConnect(connectionOpts = {}) {
36
+ await this.connect({ ...connectionOpts, persists: true });
37
+ }
38
+
39
+ async connect(connectionOpts = {}) {
40
+ const clientOpts = {
41
+ host: connectionOpts.host ? connectionOpts.host : this.clientOpts.host,
42
+ user: connectionOpts.user ? connectionOpts.user : this.clientOpts.user,
43
+ password: connectionOpts.password
44
+ ? connectionOpts.password
45
+ : this.clientOpts.password,
46
+ database: connectionOpts.database
47
+ ? connectionOpts.database
48
+ : this.clientOpts.database,
49
+ };
50
+
51
+ const persistentConn = connectionOpts.persists || this.clientOpts.persists;
52
+
53
+ logger.debug(`${FILE}::PREPARING DB CONNECTION`, {
54
+ clientOpts,
55
+ persistentConn,
56
+ });
57
+
58
+ const conn = await mysql.createConnection(clientOpts);
59
+ conn.config.namedPlaceholders = true;
60
+ logger.debug(`${FILE}::DB CONNECTED`);
61
+
62
+ if (persistentConn) {
63
+ this.conn = conn;
64
+ }
65
+
66
+ return conn;
67
+ }
68
+
69
+ /* eslint-disable-next-line class-methods-use-this */
70
+ async end(conn = {}) {
71
+ logger.debug(`${FILE}::ENDING DB CONNECTION`);
72
+ if (!isEmpty(conn)) {
73
+ await conn.end();
74
+ logger.debug(`${FILE}::DB DISCONNECTED`);
75
+ } else {
76
+ await this.conn.end();
77
+ logger.debug(`${FILE}::PERSISTED DB DISCONNECTED`);
78
+ }
79
+ }
80
+
81
+ async query(sql, sqlParams, connectionOpts = {}) {
82
+ let conn = {};
83
+ if (!isEmpty(connectionOpts)) {
84
+ conn = await this.connect(connectionOpts);
85
+ } else if (isEmpty(this.conn)) {
86
+ conn = await this.connect();
87
+ } else {
88
+ conn = this.conn;
89
+ }
90
+
91
+ try {
92
+ logger.debug(`${FILE}::QUERYING_DB`, { sql, sqlParams });
93
+ const [results, fields] = await conn.execute(sql, sqlParams);
94
+ logger.debug(`${FILE}::DB_RESPONSE`, { results, fields });
95
+
96
+ return { results, fields };
97
+ } catch (err) {
98
+ throw new LesgoException(
99
+ 'Exception caught executing SQL Statement',
100
+ `${FILE}::QUERY_EXECUTION_EXCEPTION`,
101
+ 500,
102
+ { err }
103
+ );
104
+ } finally {
105
+ if (isEmpty(this.conn) || !isEmpty(connectionOpts)) await this.end(conn);
106
+ }
107
+ }
108
+
109
+ async select(sql, sqlParams, connectionOpts = {}) {
110
+ const resp = await this.query(sql, sqlParams, connectionOpts);
111
+ return resp.results;
112
+ }
113
+
114
+ async selectFirst(sql, sqlParams, connectionOpts = {}) {
115
+ const resp = await this.query(sql, sqlParams, connectionOpts);
116
+ return resp.results[0];
117
+ }
118
+
119
+ async selectPaginate(
120
+ sql,
121
+ sqlParams,
122
+ perPage = 10,
123
+ currentPage = 1,
124
+ total = null,
125
+ connectionOpts = {}
126
+ ) {
127
+ let paginator;
128
+ if (typeof total === 'number') {
129
+ paginator = new LengthAwarePaginator(
130
+ this,
131
+ sql,
132
+ sqlParams,
133
+ {
134
+ perPage,
135
+ currentPage,
136
+ total,
137
+ },
138
+ connectionOpts
139
+ );
140
+ } else {
141
+ paginator = new Paginator(
142
+ this,
143
+ sql,
144
+ sqlParams,
145
+ {
146
+ perPage,
147
+ currentPage,
148
+ },
149
+ connectionOpts
150
+ );
151
+ }
152
+
153
+ return (await paginator).toObject();
154
+ }
155
+
156
+ async insert(sql, sqlParams, connectionOpts = {}) {
157
+ const resp = await this.query(sql, sqlParams, connectionOpts);
158
+
159
+ if (resp.results.affectedRows <= 0) {
160
+ throw new LesgoException(
161
+ 'No records inserted from INSERT query',
162
+ `${FILE}::NO_RECORDS_INSERTED`,
163
+ 400,
164
+ { resp, sql, sqlParams }
165
+ );
166
+ }
167
+
168
+ return resp.results.insertId;
169
+ }
170
+
171
+ async update(sql, sqlParams, connectionOpts = {}) {
172
+ const resp = await this.query(sql, sqlParams, connectionOpts);
173
+
174
+ if (resp.results.changedRows <= 0) {
175
+ logger.warn(`${FILE}::No records updated from UPDATE query`, {
176
+ sql,
177
+ sqlParams,
178
+ });
179
+ }
180
+
181
+ return Promise.resolve();
182
+ }
183
+ }
@@ -39,22 +39,17 @@ class ElasticsearchService {
39
39
  }
40
40
 
41
41
  search(body) {
42
- return new Promise((resolve, reject) => {
43
- const param = {
44
- index: this.index,
45
- type: this.type,
46
- body,
47
- };
48
- this.client.search(param, (err, response) => {
49
- /* istanbul ignore next */
50
- if (err) {
51
- reject(err);
52
- }
53
-
54
- this.result = response;
55
-
56
- resolve(response);
57
- });
42
+ const param = {
43
+ index: this.index,
44
+ type: this.type,
45
+ body,
46
+ };
47
+
48
+ return this.client.search(param, (err, response) => {
49
+ if (err) return Promise.reject(err);
50
+
51
+ this.result = response;
52
+ return Promise.resolve(response);
58
53
  });
59
54
  }
60
55
 
@@ -75,11 +70,9 @@ class ElasticsearchService {
75
70
  body: settings,
76
71
  };
77
72
 
78
- return new Promise((resolve, reject) => {
79
- this.client.indices.create(params, (err, response) => {
80
- // eslint-disable-next-line no-unused-expressions
81
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
82
- });
73
+ return this.client.indices.create(params, (err, response) => {
74
+ if (err) return Promise.reject(err);
75
+ return Promise.resolve(response);
83
76
  });
84
77
  }
85
78
 
@@ -89,31 +82,27 @@ class ElasticsearchService {
89
82
  ...options,
90
83
  };
91
84
 
92
- return new Promise((resolve, reject) => {
93
- this.client.indices.delete(params, (err, response) => {
94
- // eslint-disable-next-line no-unused-expressions
95
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
96
- });
85
+ return this.client.indices.delete(params, (err, response) => {
86
+ if (err) return Promise.reject(err);
87
+ return Promise.resolve(response);
97
88
  });
98
89
  }
99
90
 
100
91
  existIndices(index, options = {}) {
101
92
  const params = { index, ...options };
102
- return new Promise((resolve, reject) => {
103
- this.client.indices.exists(params, (err, response) => {
104
- // eslint-disable-next-line no-unused-expressions
105
- err ? /* istanbul ignore next */ reject(err) : resolve(response.body);
106
- });
93
+
94
+ return this.client.indices.exists(params, (err, response) => {
95
+ if (err) return Promise.reject(err);
96
+ return Promise.resolve(response.body);
107
97
  });
108
98
  }
109
99
 
110
100
  putMapping(index, type, body) {
111
101
  const params = { index, type, body: { properties: body } };
112
- return new Promise((resolve, reject) => {
113
- this.client.indices.putMapping(params, (err, response) => {
114
- // eslint-disable-next-line no-unused-expressions
115
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
116
- });
102
+
103
+ return this.client.indices.putMapping(params, (err, response) => {
104
+ if (err) return Promise.reject(err);
105
+ return Promise.resolve(response);
117
106
  });
118
107
  }
119
108
 
@@ -124,11 +113,9 @@ class ElasticsearchService {
124
113
  id,
125
114
  };
126
115
 
127
- return new Promise((resolve, reject) => {
128
- this.client.get(params, (err, response) => {
129
- // eslint-disable-next-line no-unused-expressions
130
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
131
- });
116
+ return this.client.get(params, (err, response) => {
117
+ if (err) return Promise.reject(err);
118
+ return Promise.resolve(response);
132
119
  });
133
120
  }
134
121
 
@@ -141,24 +128,20 @@ class ElasticsearchService {
141
128
  refresh,
142
129
  };
143
130
 
144
- return new Promise((resolve, reject) => {
145
- this.client.index(params, (err, response) => {
146
- // eslint-disable-next-line no-unused-expressions
147
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
148
- });
131
+ return this.client.index(params, (err, response) => {
132
+ if (err) return Promise.reject(err);
133
+ return Promise.resolve(response);
149
134
  });
150
135
  }
151
136
 
152
137
  bulkIndex(bodies) {
153
- return new Promise((resolve, reject) => {
154
- this.client.bulk(
155
- { body: this.constructBulkIndex(bodies) },
156
- (err, response) => {
157
- // eslint-disable-next-line no-unused-expressions
158
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
159
- }
160
- );
161
- });
138
+ return this.client.bulk(
139
+ { body: this.constructBulkIndex(bodies) },
140
+ (err, response) => {
141
+ if (err) return Promise.reject(err);
142
+ return Promise.resolve(response);
143
+ }
144
+ );
162
145
  }
163
146
 
164
147
  create(id, body) {
@@ -169,11 +152,9 @@ class ElasticsearchService {
169
152
  body,
170
153
  };
171
154
 
172
- return new Promise((resolve, reject) => {
173
- this.client.index(params, (err, response) => {
174
- // eslint-disable-next-line no-unused-expressions
175
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
176
- });
155
+ return this.client.index(params, (err, response) => {
156
+ if (err) return Promise.reject(err);
157
+ return Promise.resolve(response);
177
158
  });
178
159
  }
179
160
 
@@ -184,11 +165,9 @@ class ElasticsearchService {
184
165
  id,
185
166
  };
186
167
 
187
- return new Promise((resolve, reject) => {
188
- this.client.get(params, (err, response) => {
189
- // eslint-disable-next-line no-unused-expressions
190
- err ? /* istanbul ignore next */ reject(err) : resolve(response);
191
- });
168
+ return this.client.get(params, (err, response) => {
169
+ if (err) return Promise.reject(err);
170
+ return Promise.resolve(response);
192
171
  });
193
172
  }
194
173
 
@@ -0,0 +1,278 @@
1
+ import mysql from 'mysql2/promise';
2
+ import AuroraDbRDSProxyService from '../AuroraDbRDSProxyService';
3
+ import LesgoException from '../../exceptions/LesgoException';
4
+
5
+ const auroraConfig = {
6
+ host: 'some-fake-host',
7
+ user: 'fakeUsername',
8
+ password: 'fakePassword',
9
+ database: 'fakeDbName',
10
+ };
11
+
12
+ const customConfig = {
13
+ host: 'some-fake-host-2',
14
+ user: 'fakeUsername2',
15
+ password: 'fakePassword2',
16
+ database: 'fakeDbName2',
17
+ };
18
+
19
+ describe('test AuroraDbRDSProxyService instantiate', () => {
20
+ it('should not throw exception when instantiating', () => {
21
+ const db = new AuroraDbRDSProxyService(auroraConfig);
22
+ expect(db.clientOpts).toMatchObject(auroraConfig);
23
+ });
24
+
25
+ it('should allow for empty config', () => {
26
+ const db = new AuroraDbRDSProxyService();
27
+ expect(db.clientOpts).toMatchObject({});
28
+ });
29
+ });
30
+
31
+ describe('test AuroraDbRDSProxyService connect', () => {
32
+ it('should be able to connect without custom config', async () => {
33
+ const db = new AuroraDbRDSProxyService(auroraConfig);
34
+ const conn = await db.connect();
35
+
36
+ expect(conn.mocked).toMatchObject(auroraConfig);
37
+ });
38
+
39
+ it('should be able to connect with custom config', async () => {
40
+ const db = new AuroraDbRDSProxyService(auroraConfig);
41
+ const conn = await db.connect(customConfig);
42
+
43
+ expect(conn.mocked).toMatchObject(customConfig);
44
+ });
45
+ });
46
+
47
+ describe('test AuroraDbRDSProxyService query', () => {
48
+ it('should return results object when calling query function', async () => {
49
+ const db = new AuroraDbRDSProxyService(auroraConfig);
50
+ const resp = await db.query('SELECT_QUERY', {});
51
+
52
+ expect(resp).toMatchObject({
53
+ results: [
54
+ {
55
+ id: 1,
56
+ uid: 'some-uid-1',
57
+ },
58
+ {
59
+ id: 2,
60
+ uid: 'some-uid-2',
61
+ },
62
+ ],
63
+ });
64
+
65
+ expect(mysql.createConnection).toHaveBeenCalled();
66
+ });
67
+
68
+ it('should return results object when calling querying with connection options passed', async () => {
69
+ const db = new AuroraDbRDSProxyService(auroraConfig);
70
+ const resp = await db.query('SELECT_QUERY', {}, auroraConfig);
71
+
72
+ expect(resp).toMatchObject({
73
+ results: [
74
+ {
75
+ id: 1,
76
+ uid: 'some-uid-1',
77
+ },
78
+ {
79
+ id: 2,
80
+ uid: 'some-uid-2',
81
+ },
82
+ ],
83
+ });
84
+
85
+ expect(mysql.createConnection).toHaveBeenCalled();
86
+ });
87
+
88
+ it('should return results object when calling querying with no connection options passed and persistent conn', async () => {
89
+ const db = new AuroraDbRDSProxyService(auroraConfig);
90
+ await db.pConnect();
91
+ const resp = await db.query('SELECT_QUERY', {});
92
+
93
+ expect(resp).toMatchObject({
94
+ results: [
95
+ {
96
+ id: 1,
97
+ uid: 'some-uid-1',
98
+ },
99
+ {
100
+ id: 2,
101
+ uid: 'some-uid-2',
102
+ },
103
+ ],
104
+ });
105
+
106
+ expect(mysql.createConnection).toHaveBeenCalled();
107
+ });
108
+ });
109
+
110
+ describe('test AuroraDbRDSProxyService select', () => {
111
+ it('should return array results when calling select function', async () => {
112
+ const db = new AuroraDbRDSProxyService(auroraConfig);
113
+ return expect(db.select('SELECT_QUERY', {})).resolves.toMatchObject([
114
+ {
115
+ id: 1,
116
+ uid: 'some-uid-1',
117
+ },
118
+ {
119
+ id: 2,
120
+ uid: 'some-uid-2',
121
+ },
122
+ ]);
123
+ });
124
+
125
+ it('should throw an exception when writing with invalid query', async () => {
126
+ const error = new LesgoException(
127
+ 'Exception caught executing SQL Statement',
128
+ 'AURORADBSERVICE_QUERY_EXCEPTION',
129
+ 500,
130
+ {
131
+ err: {
132
+ code: 'BadRequestException',
133
+ },
134
+ }
135
+ );
136
+
137
+ const db = new AuroraDbRDSProxyService(auroraConfig);
138
+ return expect(db.select('INVALID_QUERY', {})).rejects.toMatchObject(error);
139
+ });
140
+
141
+ it('should throw an exception when calling select with invalid query parameters', async () => {
142
+ const error = new LesgoException(
143
+ 'Exception caught executing SQL Statement',
144
+ 'AURORADBSERVICE_QUERY_EXCEPTION',
145
+ 500,
146
+ {
147
+ err: {
148
+ code: 'BadRequestException',
149
+ },
150
+ }
151
+ );
152
+
153
+ const db = new AuroraDbRDSProxyService(auroraConfig);
154
+ return expect(
155
+ db.select('RANDOM_QUERY', 'INVALID_QUERY_PARAMETERS')
156
+ ).rejects.toMatchObject(error);
157
+ });
158
+ });
159
+
160
+ describe('test AuroraDbRDSProxyService selectPaginate', () => {
161
+ it('should return paginated results when calling selectPaginate function', async () => {
162
+ const db = new AuroraDbRDSProxyService(auroraConfig);
163
+ return expect(db.selectPaginate('SELECT_QUERY', {})).resolves.toMatchObject(
164
+ {
165
+ count: 2,
166
+ previous_page: false,
167
+ current_page: 1,
168
+ next_page: false,
169
+ per_page: 10,
170
+ items: [
171
+ {
172
+ id: 1,
173
+ uid: 'some-uid-1',
174
+ },
175
+ {
176
+ id: 2,
177
+ uid: 'some-uid-2',
178
+ },
179
+ ],
180
+ }
181
+ );
182
+ });
183
+
184
+ it('should return paginated results when calling selectPaginate with defined total', async () => {
185
+ const db = new AuroraDbRDSProxyService(auroraConfig);
186
+ return expect(
187
+ db.selectPaginate('SELECT_QUERY', {}, 1, 2, 1)
188
+ ).resolves.toMatchObject({
189
+ count: 1,
190
+ previous_page: 1,
191
+ current_page: 2,
192
+ next_page: 3,
193
+ per_page: 1,
194
+ items: [
195
+ {
196
+ id: 1,
197
+ uid: 'some-uid-1',
198
+ },
199
+ ],
200
+ });
201
+ });
202
+ });
203
+
204
+ describe('test AuroraDbRDSProxyService selectFirst', () => {
205
+ it('should only return the first record when calling selectFirst', async () => {
206
+ const db = new AuroraDbRDSProxyService(auroraConfig);
207
+ return expect(db.selectFirst('SELECT_QUERY', {})).resolves.toMatchObject({
208
+ id: 1,
209
+ uid: 'some-uid-1',
210
+ });
211
+ });
212
+ });
213
+
214
+ describe('test AuroraDbRDSProxyService insert', () => {
215
+ it('should return recordId when inserting record', async () => {
216
+ const db = new AuroraDbRDSProxyService(auroraConfig);
217
+ return expect(db.insert('INSERT_QUERY', {})).resolves.toEqual(20);
218
+ });
219
+
220
+ it('should throw exception when calling insert with invalid query', async () => {
221
+ const error = new LesgoException(
222
+ 'No records inserted from INSERT query',
223
+ 'AURORADBSERVICE_NO_RECORDS_INSERTED',
224
+ 400
225
+ );
226
+
227
+ const db = new AuroraDbRDSProxyService(auroraConfig);
228
+ return expect(db.insert('INVALID_INSERT_QUERY', {})).rejects.toMatchObject(
229
+ error
230
+ );
231
+ });
232
+ });
233
+
234
+ describe('test AuroraDbRDSProxyService update', () => {
235
+ it('should return success when making update query', async () => {
236
+ const db = new AuroraDbRDSProxyService(auroraConfig);
237
+ return expect(db.update('UPDATE_QUERY', {})).resolves.not.toThrow();
238
+ });
239
+
240
+ it('should throw not exception when caliing update with invalid query', async () => {
241
+ const db = new AuroraDbRDSProxyService(auroraConfig);
242
+ return expect(db.update('INVALID_UPDATE_QUERY', {})).resolves.not.toThrow();
243
+ });
244
+ });
245
+
246
+ describe('test AuroraDbRDSProxyService pConnect', () => {
247
+ it('should be able to connect without custom config', async () => {
248
+ const db = new AuroraDbRDSProxyService(auroraConfig);
249
+ await db.pConnect();
250
+
251
+ expect(db.conn.mocked).toMatchObject(auroraConfig);
252
+ });
253
+
254
+ it('should be able to connect with custom config', async () => {
255
+ const db = new AuroraDbRDSProxyService(auroraConfig);
256
+ await db.pConnect(customConfig);
257
+
258
+ expect(db.conn.mocked).toMatchObject(customConfig);
259
+ });
260
+ });
261
+
262
+ describe('test AuroraDbRDSProxyService end', () => {
263
+ it('should end the connection when passed', async () => {
264
+ const db = new AuroraDbRDSProxyService(auroraConfig);
265
+ const conn = await db.connect();
266
+ await db.end(conn);
267
+
268
+ expect(conn.end).toHaveBeenCalledTimes(1);
269
+ });
270
+
271
+ it('should end the persisted connection when none is passed', async () => {
272
+ const db = new AuroraDbRDSProxyService(auroraConfig);
273
+ await db.pConnect();
274
+ await db.end();
275
+
276
+ expect(db.conn.end).toHaveBeenCalledTimes(1);
277
+ });
278
+ });
@@ -32,6 +32,8 @@ describe('test LengthAwarePaginator instantiate', () => {
32
32
  expect(await paginator.lastItem()).toMatchObject(mockDataLastItem);
33
33
  expect(paginator.perPage()).toEqual(5);
34
34
  expect(await paginator.total()).toEqual(30);
35
+
36
+ expect(db.select).toHaveBeenCalled();
35
37
  });
36
38
  it('should not throw exception when instantiating with current page', async () => {
37
39
  const paginator = new LengthAwarePaginator(
@@ -51,6 +53,8 @@ describe('test LengthAwarePaginator instantiate', () => {
51
53
  expect(await paginator.lastItem()).toMatchObject(mockDataLastItem);
52
54
  expect(paginator.perPage()).toEqual(5);
53
55
  expect(await paginator.total()).toEqual(30);
56
+
57
+ expect(db.select).toHaveBeenCalled();
54
58
  });
55
59
  it('should default perPage to 10 when instantiating without perPage', async () => {
56
60
  const paginator = new LengthAwarePaginator(
@@ -66,6 +70,8 @@ describe('test LengthAwarePaginator instantiate', () => {
66
70
  expect(paginator.currentPage()).toEqual(1);
67
71
  expect(paginator.perPage()).toEqual(10);
68
72
  expect(await paginator.total()).toEqual(30);
73
+
74
+ expect(db.select).toHaveBeenCalled();
69
75
  });
70
76
  it('should throw exception if total is not a number', async () => {
71
77
  try {
@@ -116,6 +122,43 @@ describe('test LengthAwarePaginator instantiate', () => {
116
122
  { ...mockDataLastItem },
117
123
  ],
118
124
  });
125
+
126
+ expect(db.select).toHaveBeenCalled();
127
+ });
128
+
129
+ it('should simply return an empty paginator object if total is explicitly zero', async () => {
130
+ const paginator = new LengthAwarePaginator(
131
+ db,
132
+ 'SELECT * FROM tests',
133
+ {},
134
+ {
135
+ perPage: 5,
136
+ currentPage: 1,
137
+ total: 0,
138
+ }
139
+ );
140
+
141
+ expect(await paginator.count()).toEqual(0);
142
+ expect(await paginator.previousPage()).toEqual(false);
143
+ expect(paginator.currentPage()).toEqual(1);
144
+ expect(await paginator.nextPage()).toEqual(false);
145
+ expect(await paginator.firstItem()).toBe(undefined);
146
+ expect(await paginator.lastItem()).toBe(undefined);
147
+ expect(paginator.perPage()).toEqual(5);
148
+ expect(await paginator.total()).toEqual(0);
149
+
150
+ expect(await paginator.toObject()).toMatchObject({
151
+ count: 0,
152
+ previous_page: false,
153
+ current_page: 1,
154
+ next_page: false,
155
+ per_page: 5,
156
+ last_page: 0,
157
+ total: 0,
158
+ items: [],
159
+ });
160
+
161
+ expect(db.select).not.toHaveBeenCalled();
119
162
  });
120
163
  });
121
164
 
@@ -138,7 +181,7 @@ describe('test total() usage', () => {
138
181
  });
139
182
 
140
183
  describe('test lastPage() usage', () => {
141
- it('should get the last page using supplied paramater as total data', async () => {
184
+ it('should get the last page using supplied parameter as total data', async () => {
142
185
  const paginator1 = new LengthAwarePaginator(
143
186
  db,
144
187
  'SELECT * FROM total_tests',
@@ -12,8 +12,9 @@ export default class LengthAwarePaginator extends Paginator {
12
12
  * @param sql
13
13
  * @param sqlParams
14
14
  * @param options
15
+ * @param connection
15
16
  */
16
- constructor(db, sql, sqlParams, options) {
17
+ constructor(db, sql, sqlParams, options, connection = {}) {
17
18
  const validFields = [{ key: 'total', type: 'number', required: true }];
18
19
 
19
20
  let validated = {};
@@ -33,7 +34,7 @@ export default class LengthAwarePaginator extends Paginator {
33
34
 
34
35
  const { total } = validated;
35
36
 
36
- super(db, sql, sqlParams, options);
37
+ super(db, sql, sqlParams, options, connection);
37
38
  this.totalProp = total;
38
39
  }
39
40
 
@@ -12,13 +12,14 @@ export default class Paginator {
12
12
  * @param sqlParams
13
13
  * @param options
14
14
  */
15
- constructor(db, sql, sqlParams, options = {}) {
15
+ constructor(db, sql, sqlParams, options = {}, connection = {}) {
16
16
  const validFields = [
17
17
  { key: 'db', type: 'object', required: true },
18
18
  { key: 'sql', type: 'string', required: true },
19
19
  { key: 'sqlParams', type: 'object', required: true },
20
20
  { key: 'perPage', type: 'number', required: false },
21
21
  { key: 'currentPage', type: 'number', required: false },
22
+ { key: 'connection', type: 'object', required: false },
22
23
  ];
23
24
 
24
25
  let validated = {};
@@ -29,6 +30,7 @@ export default class Paginator {
29
30
  sql,
30
31
  sqlParams,
31
32
  ...options,
33
+ connection,
32
34
  },
33
35
  validFields
34
36
  );
@@ -56,6 +58,8 @@ export default class Paginator {
56
58
 
57
59
  this.response = [];
58
60
  this.totalProp = false;
61
+
62
+ this.connection = connection;
59
63
  }
60
64
 
61
65
  /**
@@ -213,10 +217,17 @@ export default class Paginator {
213
217
  }
214
218
 
215
219
  async executeQuery() {
216
- this.response = await this.dbProp.select(
217
- this.generatePaginationSqlSnippet(),
218
- this.sqlParamsProp
219
- );
220
+ const total = this.totalProp;
221
+ if (
222
+ (typeof total === 'number' && total > 0) ||
223
+ (typeof total !== 'number' && !total)
224
+ ) {
225
+ this.response = await this.dbProp.select(
226
+ this.generatePaginationSqlSnippet(),
227
+ this.sqlParamsProp,
228
+ this.connection
229
+ );
230
+ }
220
231
 
221
232
  this.hasNext = this.response.length > this.perPage();
222
233
  if (this.hasNext) {
@@ -232,7 +243,11 @@ export default class Paginator {
232
243
  * @returns {Promise<number>}
233
244
  */
234
245
  async countTotalItems() {
235
- const resp = await this.dbProp.select(this.sqlProp, this.sqlParamsProp);
246
+ const resp = await this.dbProp.select(
247
+ this.sqlProp,
248
+ this.sqlParamsProp,
249
+ this.connection
250
+ );
236
251
  this.totalProp = Object.keys(resp).length;
237
252
 
238
253
  return this.totalProp;
@@ -1,5 +1,12 @@
1
1
  import config from 'Config/db'; // eslint-disable-line import/no-unresolved
2
- import db from '../db';
2
+
3
+ let db;
4
+
5
+ beforeEach(() => {
6
+ jest.isolateModules(() => {
7
+ db = require('../db').default; // eslint-disable-line global-require
8
+ });
9
+ });
3
10
 
4
11
  describe('test db utils instantiate', () => {
5
12
  it('should not throw error on instantiating AuroraDbService', () => {
@@ -29,4 +36,36 @@ describe('test db utils instantiate', () => {
29
36
  },
30
37
  });
31
38
  });
39
+
40
+ it('should update AuroraDb credentials on connect based on dataApi config', () => {
41
+ config.default = 'dataApi';
42
+ let thisDb;
43
+ jest.isolateModules(() => {
44
+ thisDb = require('../db').default; // eslint-disable-line global-require
45
+ });
46
+
47
+ return expect(thisDb.client).toMatchObject({
48
+ mocked: {
49
+ database: config.connections.dataApi.database,
50
+ resourceArn: config.connections.dataApi.resourceArn,
51
+ secretArn: config.connections.dataApi.secretArn,
52
+ },
53
+ });
54
+ });
55
+
56
+ it('should update AuroraDb credentials on connect based on rdsProxy config', () => {
57
+ config.default = 'rdsProxy';
58
+ let thisDb;
59
+ jest.isolateModules(() => {
60
+ thisDb = require('../db').default; // eslint-disable-line global-require
61
+ });
62
+
63
+ return expect(thisDb.clientOpts).toMatchObject({
64
+ database: config.connections.rdsProxy.database,
65
+ host: config.connections.rdsProxy.host,
66
+ user: config.connections.rdsProxy.user,
67
+ password: config.connections.rdsProxy.password,
68
+ persists: config.connections.rdsProxy.persists,
69
+ });
70
+ });
32
71
  });
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;
@@ -4,7 +4,7 @@ import LoggerService from '../services/LoggerService';
4
4
  const transports = [
5
5
  {
6
6
  logType: 'console',
7
- level: app.debug ? 'debug' : 'info',
7
+ level: /* istanbul ignore next */ app.debug ? 'debug' : 'info',
8
8
  config: {
9
9
  getCreatedAt: true,
10
10
  tags: {