@mimik/request-retry 1.0.1 → 2.0.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.
package/.eslintrc CHANGED
@@ -1,21 +1,33 @@
1
- // Use this file as a starting point for your project's .eslintrc.
2
- // Copy this file, and add rule overrides as needed.
3
1
  {
2
+ "plugins": [
3
+ "@mimik/document-env",
4
+ "@mimik/dependencies"
5
+ ],
4
6
  "env": {
5
7
  "node": true
6
8
  },
9
+ "parserOptions": {
10
+ "ecmaVersion": 2020
11
+ },
7
12
  "extends": "airbnb",
8
13
  "rules": {
14
+ "import/no-extraneous-dependencies": ["error", {"devDependencies": true}],
15
+ "import/no-unresolved": ["error", { "amd": true, "commonjs": true, "caseSensitiveStrict": true }],
9
16
  "brace-style": [1, "stroustrup", {"allowSingleLine": true}],
10
- "no-confusing-arrow": [0],
17
+ "no-confusing-arrow": [0], // arrow isnt confusing
11
18
  "max-len": [1, 180, { "ignoreComments": true }],
12
19
  "linebreak-style": 0,
13
20
  "quotes": [1, "single"],
14
- "semi": [1, "always"]
21
+ "semi": [1, "always"],
22
+ "no-process-env": ["error"],
23
+ "@mimik/document-env/validate-document-env": 2,
24
+ "@mimik/dependencies/case-sensitive": 2,
25
+ "@mimik/dependencies/no-cycles": 2,
26
+ "@mimik/dependencies/require-json-ext": 2
15
27
  },
16
28
  "settings":{
17
29
  "react": {
18
- "version": "latest"
30
+ "version": "detect"
19
31
  }
20
32
  },
21
33
  "globals": {
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npm run commit-ready
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npm run test
package/README.md CHANGED
@@ -15,7 +15,7 @@ Make a request with retry.
15
15
  **Category**: async
16
16
  **Throws**:
17
17
 
18
- - <code>Promise</code> Will throw an error generated by getRichError encapsulating an error generated by [request-promise](https://www.npmjs.com/package/request-promise) or a TimeoutError.
18
+ - <code>Promise</code> Will throw an error generated by `getRichError` encapsulating an error generated by [request-promise](https://www.npmjs.com/package/request-promise) or a TimeoutError.
19
19
 
20
20
  The following properties are added to the `options` object under the `retry` property:
21
21
  - retries `{int}`: maximum number of retries independent to the retryStrategy. The default is `2`. If the value is less than `0` the value is set to `0`, and if the value is more that `15` the value is set to `15`,
@@ -38,11 +38,19 @@ defaultRetry = (...args) => {
38
38
  return (statusCode && (Math.floor(statusCode / 100) === 5 || statusCode === 429)) || (!statusCode && !args[1].message.includes('Invalid URI'));
39
39
  };
40
40
  ```
41
+ If logLevel is empty, request and response will be logged as info and the response will be in response property with full details. Error on the request will generate a warning. If logLevel is not empty but not complete, logLevel will take control over default.
42
+
43
+ If not alredy set,the user agent will the set to `mimik-{serverType}/{serverVersion}/{serverId} {architecture} {node}`;
44
+
45
+ To facilitate the transition from request-promise the following action are taken on options:
46
+ - `uri` takes precednce on `url`
47
+ - `body` takes precedence on `json` if `json` is an object and are used to assign `data`
48
+ - `qs` is used to assign to `params`
41
49
 
42
50
  **Requires**: <code>module:@mimik/sumologic-winston-logger</code>, <code>module:@mimik/response-helper</code>
43
- **Fulfil**: <code>object</code> - Response of the [request-promise](https://www.npmjs.com/package/request-promise) request.
51
+ **Fulfil**: <code>object</code> - Response of the [axios](https://www.npmjs.com/package/axios) response with option `resolveWithFullResponse` set to true otherwise only `response.data` is returned.
44
52
 
45
53
  | Param | Type | Description |
46
54
  | --- | --- | --- |
47
- | options | <code>object</code> | Options for the request. Similar to [request-promise](https://www.npmjs.com/package/request-promise) options. |
55
+ | options | <code>object</code> | Options for the request. Similar to [axios](https://www.npmjs.com/package/axios) options. `validateStatus` options is disabled, an error will be created for statusCode outside of [200, 300[. The options `resolveWithFullResponse` is added and if set to true, the response will be a full axios response. If set to false or missing only `reponse.data` will be returned. |
48
56
 
package/index.js CHANGED
@@ -1,15 +1,22 @@
1
- const rp = require('request-promise');
2
1
  const Promise = require('bluebird');
2
+ const axios = require('axios');
3
3
 
4
4
  const logger = require('@mimik/sumologic-winston-logger');
5
5
  const { getRichError } = require('@mimik/response-helper');
6
-
6
+ const { getCorrelationId, getUserAgent } = require('@mimik/request-helper');
7
7
  const {
8
8
  validateOptions,
9
9
  calculateDelay,
10
10
  isRetry,
11
11
  setResponse,
12
12
  } = require('./lib/validate');
13
+ const { rp } = require('./lib/rp-axios-wrapper');
14
+
15
+ const DEFAULT_RESPONSE_NAME = 'response';
16
+ const DEFAULT_LOGLEVEL_DETAILS = 'full';
17
+ const DEFAULT_LOGLEVEL_RESPONSE = 'info';
18
+ const DEFAULT_LOGLEVEL_ERROR = 'warn';
19
+ const DEFAULT_LOGLEVEL_REQUEST = 'info';
13
20
 
14
21
  Promise.config({ cancellation: true });
15
22
 
@@ -26,10 +33,10 @@ Promise.config({ cancellation: true });
26
33
  * @requires @mimik/sumologic-winston-logger
27
34
  * @requires @mimik/response-helper
28
35
  * @category async
29
- * @param {object} options - Options for the request. Similar to [request-promise](https://www.npmjs.com/package/request-promise) options.
36
+ * @param {object} options - Options for the request. Similar to [axios](https://www.npmjs.com/package/axios) options. `validateStatus` options is disabled, an error will be created for statusCode outside of [200, 300[. The options `resolveWithFullResponse` is added and if set to true, the response will be a full axios response. If set to false or missing only `reponse.data` will be returned.
30
37
  * @return {Promise}.
31
- * @fulfil {object} - Response of the [request-promise](https://www.npmjs.com/package/request-promise) request.
32
- * @throws {Promise} Will throw an error generated by getRichError encapsulating an error generated by [request-promise](https://www.npmjs.com/package/request-promise) or a TimeoutError.
38
+ * @fulfil {object} - Response of the [axios](https://www.npmjs.com/package/axios) response with option `resolveWithFullResponse` set to true otherwise only `response.data` is returned.
39
+ * @throws {Promise} Will throw an error generated by `getRichError` encapsulating an error generated by [request-promise](https://www.npmjs.com/package/request-promise) or a TimeoutError.
33
40
  *
34
41
  * The following properties are added to the `options` object under the `retry` property:
35
42
  * - retries `{int}`: maximum number of retries independent to the retryStrategy. The default is `2`. If the value is less than `0` the value is set to `0`, and if the value is more that `15` the value is set to `15`,
@@ -52,32 +59,54 @@ Promise.config({ cancellation: true });
52
59
  * return (statusCode && (Math.floor(statusCode / 100) === 5 || statusCode === 429)) || (!statusCode && !args[1].message.includes('Invalid URI'));
53
60
  * };
54
61
  *```
62
+ * If logLevel is empty, request and response will be logged as info and the response will be in response property with full details. Error on the request will generate a warning. If logLevel is not empty but not complete, logLevel will take control over default.
55
63
  *
64
+ * If not alredy set,the user agent will the set to `mimik-{serverType}/{serverVersion}/{serverId} {architecture} {node}`;
65
+ *
66
+ * To facilitate the transition from request-promise the following action are taken on options:
67
+ * - `uri` takes precednce on `url`
68
+ * - `body` takes precedence on `json` if `json` is an object and are used to assign `data`
69
+ * - `qs` is used to assign to `params`
56
70
  */
57
- const rpRetry = (options) => {
58
- const correlationId = (options.headers && options.headers['x-correlation-id']);
71
+ const rpRetry = (origOptions) => {
72
+ const { CancelToken } = axios;
73
+ const source = CancelToken.source();
74
+ const options = origOptions;
75
+ const correlationId = (options.headers && options.headers['x-correlation-id']) ? options.headers['x-correlation-id'] : getCorrelationId();
59
76
  const errors = [];
60
77
  const delayPromises = [];
78
+ const criteria = validateOptions(options, correlationId);
79
+ const { logLevel } = criteria;
80
+ const logLevelLength = Object.keys(logLevel).length;
61
81
  let nbRetries = 0;
82
+ let mainTimeout;
62
83
 
63
- const retryProcess = (opts, nbRetry, crit, corrId) => rp(opts)
84
+ if (!options.headers) options.headers = { 'user-agent': getUserAgent() };
85
+ else if (!options.headers['user-agent']) options.headers['user-agent'] = getUserAgent();
86
+ options.cancelToken = source.token;
87
+
88
+ const retryProcess = (nbRetry) => rp(options)
64
89
  .then((response) => {
65
- const info = { options: opts, nbRetries, errors };
90
+ const info = { options, nbRetries, errors };
66
91
 
67
- if (crit.logLevel.response) {
68
- info[crit.logLevel.responseName] = setResponse(response, crit.logLevel.responseDetails);
69
- logger[crit.logLevel.response]('request response', info, corrId);
92
+ if (logLevel.response) {
93
+ info[logLevel.responseName] = setResponse(response, logLevel.responseDetails);
94
+ logger[logLevel.response]('request response', info, correlationId);
95
+ }
96
+ else if (logLevelLength === 0) {
97
+ info[DEFAULT_RESPONSE_NAME] = setResponse(response, DEFAULT_LOGLEVEL_DETAILS);
98
+ logger[DEFAULT_LOGLEVEL_RESPONSE]('request response', info, correlationId);
70
99
  }
71
100
  return response;
72
101
  })
73
102
  .catch((err) => {
74
- if ((nbRetry < crit.retries) && isRetry(opts, nbRetry, crit, err, corrId)) {
103
+ if ((nbRetry < criteria.retries) && isRetry(options, nbRetry, criteria, err, correlationId)) {
75
104
  nbRetries = nbRetry + 1;
76
- errors.push(err.message);
77
- const delayPromise = Promise.delay(calculateDelay(opts, nbRetry, crit, err, corrId));
105
+ errors.push(err);
106
+ const delayPromise = Promise.delay(calculateDelay(options, nbRetry, criteria, err, correlationId));
78
107
 
79
108
  delayPromises.unshift(delayPromise);
80
- return delayPromise.then(() => retryProcess(opts, nbRetry + 1, crit, corrId));
109
+ return delayPromise.then(() => retryProcess(nbRetry + 1));
81
110
  }
82
111
  const error = err;
83
112
 
@@ -88,36 +117,49 @@ const rpRetry = (options) => {
88
117
  errors,
89
118
  };
90
119
  }
91
- throw getRichError(error.statusCode, 'request error response', { options }, error, crit.logLevel.error, corrId);
120
+ throw getRichError(
121
+ error.statusCode,
122
+ 'request error response',
123
+ { options },
124
+ error,
125
+ logLevel.error || ((logLevelLength === 0) ? DEFAULT_LOGLEVEL_ERROR : undefined),
126
+ correlationId,
127
+ );
92
128
  });
93
129
 
94
- let mainTimeout;
95
- const criteria = validateOptions(options);
96
- const retryPromise = retryProcess(options, 0, criteria, correlationId);
130
+ const retryPromise = retryProcess(0);
97
131
  const mainTimeoutPromise = new Promise((resolve, reject) => {
98
132
  mainTimeout = setTimeout(() => {
99
- delayPromises.forEach((delayPromise) => delayPromise.cancel());
100
- retryPromise.cancel();
133
+ delayPromises.forEach((delayPromise) => delayPromise.cancel(`retry timeout, ${criteria.timeout}, ${nbRetries}`));
134
+ source.cancel(`retry timeout, ${criteria.timeout}, ${nbRetries}`);
101
135
  let error = new Error('retry timeout');
102
136
 
103
137
  if (nbRetries !== 0) {
104
138
  error.info = { retry: { nbRetries, errors } };
105
139
  }
106
140
  error.name = 'TimeoutError';
107
- error = getRichError('System', 'request error response', { options }, error, criteria.logLevel.error, correlationId);
141
+ error = getRichError(
142
+ 'System',
143
+ 'request error response',
144
+ { options },
145
+ error,
146
+ logLevel.error || ((logLevelLength === 0) ? DEFAULT_LOGLEVEL_ERROR : undefined),
147
+ correlationId,
148
+ );
108
149
  reject(error);
109
150
  }, criteria.timeout * 1000);
110
151
  });
111
152
 
112
- if (criteria.logLevel.request) logger[criteria.logLevel.request]('making a request', { options, criteria }, correlationId);
153
+ if (logLevel.request) logger[logLevel.request]('making a request', { options, criteria }, correlationId);
154
+ else if (logLevelLength === 0) logger[DEFAULT_LOGLEVEL_REQUEST]('making a request', { options, criteria }, correlationId);
113
155
  return Promise.race([retryPromise, mainTimeoutPromise])
114
156
  .then((result) => {
115
- mainTimeoutPromise.cancel();
157
+ mainTimeoutPromise.cancel('main timeout');
116
158
  clearTimeout(mainTimeout);
117
159
  return result;
118
160
  })
119
161
  .catch((err) => {
120
- mainTimeoutPromise.cancel();
162
+ mainTimeoutPromise.cancel(`main timeout error: ${err.message}`);
121
163
  clearTimeout(mainTimeout);
122
164
  throw err;
123
165
  });
@@ -0,0 +1,48 @@
1
+ const axios = require('axios');
2
+ const _ = require('lodash');
3
+
4
+ const { getRichError } = require('@mimik/response-helper');
5
+
6
+ const rp = (origOptions) => {
7
+ const options = _.clone(origOptions);
8
+
9
+ if (options.uri) options.url = options.uri;
10
+ delete options.uri;
11
+ delete options.validateStatus;
12
+ if (options.body) {
13
+ options.data = options.body;
14
+ delete options.body;
15
+ }
16
+ else if (typeof options.json === 'object') {
17
+ options.data = options.body;
18
+ delete options.json;
19
+ }
20
+ else delete options.json;
21
+ if (options.qs) options.params = options.qs;
22
+ return axios(options)
23
+ .then((res) => {
24
+ if (options.resolveWithFullResponse) return res;
25
+ return res.data;
26
+ })
27
+ .catch((err) => {
28
+ const { response } = err;
29
+ let errorMsg = 'no response';
30
+
31
+ if (err.message) errorMsg = `${errorMsg}: ${err.message}`;
32
+ if (!response) {
33
+ throw getRichError('System', errorMsg, {
34
+ code: err.code,
35
+ address: err.address,
36
+ port: err.port,
37
+ syscall: err.syscall,
38
+ });
39
+ }
40
+ const { data } = response;
41
+
42
+ throw getRichError(response.status, data ? data.message || response.statusText : response.statusText, data);
43
+ });
44
+ };
45
+
46
+ module.exports = {
47
+ rp,
48
+ };
package/lib/validate.js CHANGED
@@ -3,7 +3,7 @@ const logger = require('@mimik/sumologic-winston-logger');
3
3
  const DETAILS = ['full', 'type', 'count'];
4
4
  const TYPE = DETAILS[1];
5
5
  const RESPONSE = 'response';
6
- const DEFAULT_LOG_LEVEL = logger.LEVELS[5]; // silly
6
+ const DEFAULT_LOGLEVEL = logger.LEVELS[5]; // silly
7
7
  const DEFAULT_RETRIES = 2;
8
8
  const MIN_RETRIES = 0;
9
9
  const MAX_RETRIES = 15;
@@ -22,7 +22,7 @@ const defaultRetry = (...args) => {
22
22
  const unknownRetry = () => true;
23
23
  const defaultDelay = () => DEFAULT_DELAY;
24
24
 
25
- const validateOptions = (options) => {
25
+ const validateOptions = (options, correlationId) => {
26
26
  if (!options.retry) {
27
27
  return {
28
28
  delay: DEFAULT_DELAY,
@@ -44,22 +44,22 @@ const validateOptions = (options) => {
44
44
  const validateLogLevel = (val, name) => {
45
45
  if (!val[name]) return null;
46
46
  if (!logger.LEVELS.includes(val[name])) {
47
- logger.warn(`invalid logLevel.${name}, reset to default`, { name, value: val[name], default: DEFAULT_LOG_LEVEL });
48
- return DEFAULT_LOG_LEVEL;
47
+ logger.warn(`invalid logLevel.${name}, reset to default`, { name, value: val[name], default: DEFAULT_LOGLEVEL }, correlationId);
48
+ return DEFAULT_LOGLEVEL;
49
49
  }
50
50
  return val[name];
51
51
  };
52
52
  const validateValue = (val, name, defaultValue, minValue, maxValue) => {
53
53
  if (typeof val !== 'number') {
54
- if (val) logger.warn(`invalid ${name}, reset to default`, { type: typeof val, defaultValue });
54
+ if (val) logger.warn(`invalid ${name}, reset to default`, { type: typeof val, defaultValue }, correlationId);
55
55
  return defaultValue;
56
56
  }
57
57
  if (val < minValue) {
58
- logger.warn(`out of scope ${name}, reset to min`, { value: val, minValue });
58
+ logger.warn(`out of scope ${name}, reset to min`, { value: val, minValue }, correlationId);
59
59
  return minValue;
60
60
  }
61
61
  if (val > maxValue) {
62
- logger.warn(`out of scope ${name}, reset to max`, { value: val, maxValue });
62
+ logger.warn(`out of scope ${name}, reset to max`, { value: val, maxValue }, correlationId);
63
63
  return maxValue;
64
64
  }
65
65
  return val;
@@ -67,7 +67,7 @@ const validateOptions = (options) => {
67
67
  const validateResponseDetails = (val, name, defaultValue) => {
68
68
  if (!val) return defaultValue;
69
69
  if (!DETAILS.includes(val)) {
70
- logger.warn(`invalid logLevel.${name}, reset to default`, { name, value: val, defaultValue });
70
+ logger.warn(`invalid logLevel.${name}, reset to default`, { name, value: val, defaultValue }, correlationId);
71
71
  return defaultValue;
72
72
  }
73
73
  return val;
@@ -75,7 +75,7 @@ const validateOptions = (options) => {
75
75
  const validateResponseName = (val, name, defaultValue) => {
76
76
  if (!val && val !== '') return defaultValue;
77
77
  if (typeof val !== 'string' || val.length === 0 || val.trim().length === 0) {
78
- logger.warn(`invalid logLevel.${name}, reset to default`, { name, value: val, defaultValue });
78
+ logger.warn(`invalid logLevel.${name}, reset to default`, { name, value: val, defaultValue }, correlationId);
79
79
  return defaultValue;
80
80
  }
81
81
  return val;
@@ -88,14 +88,14 @@ const validateOptions = (options) => {
88
88
  if (typeof delayStrategy !== 'function') {
89
89
  if (!delayStrategy) delayStrategy = defaultDelay;
90
90
  else {
91
- logger.warn('invalid delayStrategy, using delay', { strategyType: typeof delayStrategy, delay, default: DEFAULT_DELAY });
91
+ logger.warn('invalid delayStrategy, using delay', { strategyType: typeof delayStrategy, delay, default: DEFAULT_DELAY }, correlationId);
92
92
  delayStrategy = setDelay;
93
93
  }
94
94
  }
95
95
  if (typeof retryStrategy !== 'function') {
96
96
  if (!retryStrategy) retryStrategy = defaultRetry;
97
97
  else {
98
- logger.warn('invalid retryStrategy, ignoring', { strategyType: typeof retryStrategy, using: unknownRetry });
98
+ logger.warn('invalid retryStrategy, ignoring', { strategyType: typeof retryStrategy, using: unknownRetry }, correlationId);
99
99
  retryStrategy = unknownRetry;
100
100
  }
101
101
  }
@@ -0,0 +1,16 @@
1
+ /* eslint-disable no-console */
2
+ const express = require('express');
3
+ const bodyParser = require('body-parser');
4
+
5
+ const app = express();
6
+
7
+ app.use(bodyParser.json());
8
+ app.get('/success', (req, res) => {
9
+ res.statusCode = 200;
10
+
11
+ res.send({ statusCode: 200 });
12
+ });
13
+
14
+ app.listen(9000, () => {
15
+ console.log('----->', `retry mock at ${9000}`);
16
+ });
@@ -0,0 +1,23 @@
1
+ /* eslint-disable no-console */
2
+ require('../test/testEnv');
3
+
4
+ const { rpRetry } = require('../index');
5
+
6
+ const correlationId = `--request-retry-test--/${new Date().toISOString()}`;
7
+
8
+ const options = {
9
+ method: 'GET',
10
+ headers: {
11
+ 'x-correlation-id': correlationId,
12
+ },
13
+ url: 'http://localhost:9080/test/retry',
14
+ json: true,
15
+ retry: {},
16
+ };
17
+
18
+ options.retry.delayStrategy = () => 10000;
19
+ options.retry.timeout = 10;
20
+ options.retry.retries = 2;
21
+
22
+ rpRetry(options)
23
+ .catch((err) => console.log(err));
@@ -0,0 +1,60 @@
1
+ /* eslint-disable no-console */
2
+ const express = require('express');
3
+ const bodyParser = require('body-parser');
4
+
5
+ const app = express();
6
+ const config = {
7
+ port: 9080,
8
+ path: '/retry',
9
+ base: '/test',
10
+ get: {
11
+ nbRetry: 400,
12
+ success: 200,
13
+ error: 500,
14
+ },
15
+ post: {
16
+ success: 201,
17
+ },
18
+ };
19
+ let nbRequest = 0;
20
+ let time = 0;
21
+ let message;
22
+
23
+ app.use(bodyParser.json());
24
+ app.get(`${config.base}${config.path}`, (req, res) => {
25
+ if (nbRequest === 0) message = 'Recieved first request';
26
+ else {
27
+ const timeLap = Date.now() - time;
28
+
29
+ message = `Received ${nbRequest} retry with timelap ${timeLap}`;
30
+ }
31
+ console.log('----->', message);
32
+ time = Date.now();
33
+
34
+ if (nbRequest === config.get.nbRetry) {
35
+ res.statusCode = config.get.success;
36
+ res.send({ statusCode: config.get.success });
37
+ return;
38
+ }
39
+ nbRequest += 1;
40
+ res.statusCode = config.get.error;
41
+
42
+ res.send({ statusCode: config.get.error });
43
+ });
44
+ app.post(`${config.base}${config.path}`, (req, res) => {
45
+ console.log('----->', 'Received a POST request');
46
+ res.statusCode = config.post.success;
47
+ res.send({ statusCode: config.post.success });
48
+ });
49
+
50
+ app.post('/logs/:id', (req, res) => {
51
+ console.log('--->', 'Recieved a log');
52
+ console.log('---> id:', req.params.id);
53
+ console.log('---> body:', req.body);
54
+ res.statusCode = config.post.success;
55
+ res.send({ statusCode: config.post.success });
56
+ });
57
+
58
+ app.listen(config.port, () => {
59
+ console.log('----->', `retry mock at ${config.port}`);
60
+ });
@@ -1,27 +1,28 @@
1
- const doSomething = () => {
2
- let add = 0;
1
+ /* eslint-disable no-console */
2
+ const doSomething = () => {
3
+ let add = 0;
3
4
 
4
- for (i = 0; i < 100000000; i++) {
5
- add +=1;
6
- console.log(add)
7
- };
8
- return add
5
+ for (let i = 0; i < 100000000; i += 1) {
6
+ add += 1;
7
+ console.log(add);
8
+ }
9
+ return add;
9
10
  };
10
11
  const timeout = 1; // ms
11
12
 
12
13
  const race = () => {
13
- let result;
14
- const mainTimeout = setTimeout(() => {
15
- throw new Error('timeout')
16
- }, timeout);
14
+ let result;
15
+ const mainTimeout = setTimeout(() => {
16
+ throw new Error('timeout');
17
+ }, timeout);
17
18
 
18
- try { result = doSomething() }
19
- catch (err) {
20
- console.log(err);
21
- result = 0;
22
- }
23
- clearTimeout(mainTimeout);
24
- return result;
25
- }
19
+ try { result = doSomething(); }
20
+ catch (err) {
21
+ console.log(err);
22
+ result = 0;
23
+ }
24
+ clearTimeout(mainTimeout);
25
+ return result;
26
+ };
26
27
 
27
28
  console.log(race());
@@ -1,4 +1,5 @@
1
- const { rpRetry } = require('../../../common/rp-retry-34/index.js');
1
+ /* eslint-disable no-console */
2
+ const { rpRetry } = require('../index');
2
3
 
3
4
  const options = {
4
5
  method: 'GET',
@@ -9,49 +10,50 @@ const options = {
9
10
  json: true,
10
11
  timeout: 200,
11
12
  // retry: {
12
- // all default: maxRetry: 2, retryDelay: 1000ms, maxTimeout: 50s, logLevel: 3 silly, type response, retryDelayStrategy: setDelay, retryStrategy: defaultRetry
13
- // retries: -1, // => 0
14
- // retries: 20, // => 15
15
- // retries: 'joe', // => 2
16
- // retries: 0, // OK
17
- // delay: 0, // => 20ms
18
- // delay: 100000, // => 30000ms
19
- // delay: 'joe', // => 1000ms
20
- // delay: 1, // => 20ms
21
- // delayStrategy: 'joe', // => setDelay returning retryDelay
22
- // delayStrategy: function badDelay() { return 'joe' }, // => no change then bad delay strategy => retryDelay
23
- // delayStrategy: function userDelay(...args) { return (Math.pow(2, args[0]) * 100) + Math.floor(Math.random() * 50) }, // OK
24
- // retryStrategy: 'joe', // => unknowRetry returning false
25
- // retryStrategy: function badRetry() { return 'joe' }, // => no change then bad retry strategy => false
26
- // retryStrategy: function userRetry(...args) { // OK
27
- // const { statusCode } = args[1]; // err
28
- //
29
- // return statusCode && (Math.floor(statusCode/100) !== 5 || statusCode !== 429); // retry if there is no statusCode or if there is 500 range statucCode or 429
30
- // },
31
- // retryStrategy: function invalidRetry(...args) { // defaultRetry
32
- // const { statusCode } = args[34]; // null value
33
- //
34
- // return true // no retry
35
- // },
36
- // timeout: 0, // => 10s
37
- // timeout: 150, // => 120s
38
- // timeout: 'joe', // => 50s
39
- // logLevel: {
40
- // response: 'test', // silly with warning
41
- // error: 1234, // => silly with warning
42
- // request: null, // => silly no warning
43
- // responseDetails: 'test', // => type with warning
44
- // responseName: '', // => response with warning
45
- // },
46
- // logLevel: {
47
- // response: 'info', // ok all the other value to default no warning
48
- // },
49
- //}
13
+ // all default: maxRetry: 2, retryDelay: 1000ms, maxTimeout: 50s, logLevel: 3 silly, type response, retryDelayStrategy: setDelay, retryStrategy: defaultRetry
14
+ // retries: -1, // => 0
15
+ // retries: 20, // => 15
16
+ // retries: 'joe', // => 2
17
+ // retries: 0, // OK
18
+ // delay: 0, // => 20ms
19
+ // delay: 100000, // => 30000ms
20
+ // delay: 'joe', // => 1000ms
21
+ // delay: 1, // => 20ms
22
+ // delayStrategy: 'joe', // => setDelay returning retryDelay
23
+ // delayStrategy: function badDelay() { return 'joe' }, // => no change then bad delay strategy => retryDelay
24
+ // delayStrategy: function userDelay(...args) { return (Math.pow(2, args[0]) * 100) + Math.floor(Math.random() * 50) }, // OK
25
+ // retryStrategy: 'joe', // => unknowRetry returning false
26
+ // retryStrategy: function badRetry() { return 'joe' }, // => no change then bad retry strategy => false
27
+ // retryStrategy: function userRetry(...args) { // OK
28
+ // const { statusCode } = args[1]; // err
29
+ //
30
+ // return statusCode && (Math.floor(statusCode/100) !== 5 || statusCode !== 429); // retry if there is no statusCode or if there is 500 range statucCode or 429
31
+ // },
32
+ // retryStrategy: function invalidRetry(...args) { // defaultRetry
33
+ // const { statusCode } = args[34]; // null value
34
+ //
35
+ // return true // no retry
36
+ // },
37
+ // timeout: 0, // => 10s
38
+ // timeout: 150, // => 120s
39
+ // timeout: 'joe', // => 50s
40
+ // logLevel: {
41
+ // response: 'test', // silly with warning
42
+ // error: 1234, // => silly with warning
43
+ // request: null, // => silly no warning
44
+ // responseDetails: 'test', // => type with warning
45
+ // responseName: '', // => response with warning
46
+ // },
47
+ // logLevel: {
48
+ // response: 'info', // ok all the other value to default no warning
49
+ // },
50
+ // }
50
51
  };
51
52
 
52
53
  const sDate = Date.now();
53
54
 
54
55
  rpRetry(options)
55
- .then(response => console.log('success', { response: typeof response }, Date.now() - sDate))
56
- .catch(err => { console.log('failure', { error: err, info: err.info }, Date.now() - sDate);
57
- })
56
+ .then((response) => console.log('success', { response: typeof response }, Date.now() - sDate))
57
+ .catch((err) => {
58
+ console.log('failure', { error: err, info: err.info }, Date.now() - sDate);
59
+ });
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@mimik/request-retry",
3
- "version": "1.0.1",
4
- "description": "Request retry wrapping request-promise",
3
+ "version": "2.0.8",
4
+ "description": "Request retry wrapping axios",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "lint": "gulp lint",
8
- "docs": "gulp docs",
9
- "test": "nyc gulp test",
10
- "commit-ready": "gulp docs; gulp lint; npm run test"
7
+ "lint": "eslint --ignore-path .gitignore .",
8
+ "docs": "jsdoc2md index.js > README.md",
9
+ "test": "mocha --reporter mochawesome --bail --check-leaks test/",
10
+ "test-ci": "nyc --reporter=lcov --reporter=text npm test",
11
+ "prepublishOnly": "npm run docs && npm run lint && npm run test-ci",
12
+ "commit-ready": "npm run docs && npm run lint && npm run test-ci",
13
+ "prepare": "husky install"
11
14
  },
12
15
  "husky": {
13
16
  "hooks": {
@@ -19,41 +22,39 @@
19
22
  "mimik",
20
23
  "microservice"
21
24
  ],
22
- "author": "mimik",
23
- "license": "Apache-2.0",
25
+ "author": "mimik technology inc <support@mimik.com> (https://developer.mimik.com/)",
26
+ "license": "MIT",
24
27
  "repository": {
25
28
  "type": "git",
26
29
  "url": "https://bitbucket.org/mimiktech/request-retry"
27
30
  },
28
31
  "dependencies": {
29
- "@mimik/response-helper": "2.2.1",
30
- "@mimik/sumologic-winston-logger": "1.3.0",
31
- "bluebird": "3.7.1",
32
- "request": "2.88.0",
33
- "request-promise": "4.2.5"
32
+ "@mimik/request-helper": "^1.7.5",
33
+ "@mimik/response-helper": "^2.6.1",
34
+ "@mimik/sumologic-winston-logger": "^1.6.8",
35
+ "axios": "0.27.2",
36
+ "bluebird": "3.7.2",
37
+ "lodash": "4.17.21"
34
38
  },
35
39
  "devDependencies": {
36
- "acorn": "7.1.0",
37
- "body-parser": "1.19.0",
38
- "chai": "4.2.0",
39
- "eslint": "6.6.0",
40
- "eslint-config-airbnb": "18.0.1",
41
- "eslint-plugin-import": "2.18.2",
42
- "eslint-plugin-jsx-a11y": "6.2.3",
43
- "eslint-plugin-react": "7.16.0",
44
- "eslint-plugin-react-hooks": "2.2.0",
45
- "express": "4.17.1",
46
- "fancy-log": "1.3.3",
47
- "gulp": "4.0.2",
48
- "gulp-eslint": "6.0.0",
49
- "gulp-git": "2.9.0",
50
- "gulp-spawn-mocha": "6.0.0",
51
- "husky": "3.0.9",
52
- "jsdoc-to-markdown": "5.0.2",
53
- "mocha": "6.2.2",
54
- "mochawesome": "4.1.0",
55
- "nyc": "14.1.1",
56
- "sinon": "7.5.0",
57
- "verror": "1.10.0"
40
+ "@mimik/eslint-plugin-dependencies": "^2.4.3",
41
+ "@mimik/eslint-plugin-document-env": "^1.0.3",
42
+ "acorn": "8.7.1",
43
+ "body-parser": "1.20.0",
44
+ "chai": "4.3.6",
45
+ "eslint": "8.15.0",
46
+ "eslint-config-airbnb": "19.0.4",
47
+ "eslint-plugin-import": "2.26.0",
48
+ "eslint-plugin-jsx-a11y": "6.5.1",
49
+ "eslint-plugin-react": "7.29.4",
50
+ "eslint-plugin-react-hooks": "4.5.0",
51
+ "express": "4.18.1",
52
+ "husky": "8.0.1",
53
+ "jsdoc-to-markdown": "7.1.1",
54
+ "mocha": "10.0.0",
55
+ "mochawesome": "7.1.3",
56
+ "nyc": "15.1.0",
57
+ "sinon": "14.0.0",
58
+ "verror": "1.10.1"
58
59
  }
59
60
  }
package/test/retryMock.js CHANGED
@@ -46,6 +46,14 @@ app.post(`${config.base}${config.path}`, (req, res) => {
46
46
  res.statusCode = config.post.success;
47
47
  res.send({ statusCode: config.post.success });
48
48
  });
49
+ app.get('/stop', (req, res) => {
50
+ console.log('----->', 'Received a stop');
51
+ res.statusCode = config.get.success;
52
+ res.send({ statusCode: config.get.success });
53
+ setTimeout(() => {
54
+ process.exit(0);
55
+ }, 3000);
56
+ });
49
57
 
50
58
  const listen = () => {
51
59
  app.listen(config.port, () => {
@@ -2,10 +2,11 @@ const chai = require('chai');
2
2
  const sinon = require('sinon');
3
3
  const VError = require('verror');
4
4
  const { beforeEach, afterEach } = require('mocha');
5
+ require('./testEnv');
5
6
 
6
7
  const logger = require('@mimik/sumologic-winston-logger');
8
+ const { getCorrelationId } = require('@mimik/request-helper');
7
9
 
8
- require('./testEnv');
9
10
  const mockServer = require('./retryMock');
10
11
  const { rpRetry } = require('../index');
11
12
 
@@ -16,7 +17,7 @@ chai.should();
16
17
  // const NO_ERROR = 'not an error object';
17
18
 
18
19
  describe('request-retry Unit Tests', () => {
19
- const correlationId = '--request-retry-test--';
20
+ const correlationId = getCorrelationId('--request-retry-test--');
20
21
  // let loggerSpyError;
21
22
  let loggerSpyWarn;
22
23
  let loggerSpyInfo;
@@ -261,7 +262,7 @@ describe('request-retry Unit Tests', () => {
261
262
  options.retry.retries = 2;
262
263
  return rpRetry(options)
263
264
  .catch(() => {
264
- sinon.assert.calledTwice(loggerSpyWarn);
265
+ sinon.assert.called(loggerSpyWarn);
265
266
  const callArgs = loggerSpyWarn.getCall(0).args;
266
267
  expect(callArgs[0]).to.be.a('string');
267
268
  expect(callArgs[0]).to.equal('bad retry strategy');
@@ -272,7 +273,7 @@ describe('request-retry Unit Tests', () => {
272
273
  options.retry.retries = 2;
273
274
  return rpRetry(options)
274
275
  .catch(() => {
275
- sinon.assert.calledTwice(loggerSpyWarn);
276
+ sinon.assert.called(loggerSpyWarn);
276
277
  const callArgs = loggerSpyWarn.getCall(0).args;
277
278
  expect(callArgs[0]).to.be.a('string');
278
279
  expect(callArgs[0]).to.equal('bad retry strategy');
@@ -282,10 +283,8 @@ describe('request-retry Unit Tests', () => {
282
283
  options.retry.retryStrategy = () => false;
283
284
  options.retry.retries = 2;
284
285
  return rpRetry(options)
285
- .then(() => {
286
- })
287
286
  .catch(() => {
288
- sinon.assert.notCalled(loggerSpyWarn);
287
+ sinon.assert.called(loggerSpyWarn);
289
288
  });
290
289
  });
291
290
  it('should generate warning: bad delay strategy (throwing an error), ignoring and using default delay', () => {
@@ -293,7 +292,7 @@ describe('request-retry Unit Tests', () => {
293
292
  options.retry.retries = 2;
294
293
  return rpRetry(options)
295
294
  .catch(() => {
296
- sinon.assert.calledTwice(loggerSpyWarn);
295
+ sinon.assert.called(loggerSpyWarn);
297
296
  const callArgs = loggerSpyWarn.getCall(0).args;
298
297
  expect(callArgs[0]).to.be.a('string');
299
298
  expect(callArgs[0]).to.equal('bad delay strategy');
@@ -304,7 +303,7 @@ describe('request-retry Unit Tests', () => {
304
303
  options.retry.retries = 2;
305
304
  return rpRetry(options)
306
305
  .catch(() => {
307
- sinon.assert.calledTwice(loggerSpyWarn);
306
+ sinon.assert.called(loggerSpyWarn);
308
307
  const callArgs = loggerSpyWarn.getCall(0).args;
309
308
  expect(callArgs[0]).to.be.a('string');
310
309
  expect(callArgs[0]).to.equal('bad delay strategy');
@@ -315,7 +314,7 @@ describe('request-retry Unit Tests', () => {
315
314
  options.retry.retries = 2;
316
315
  return rpRetry(options)
317
316
  .catch(() => {
318
- sinon.assert.calledTwice(loggerSpyWarn);
317
+ sinon.assert.called(loggerSpyWarn);
319
318
  const callArgs = loggerSpyWarn.getCall(0).args;
320
319
  expect(callArgs[0]).to.be.a('string');
321
320
  expect(callArgs[0]).to.equal('calculated delay out of scope: using delay or default');
@@ -325,10 +324,8 @@ describe('request-retry Unit Tests', () => {
325
324
  options.retry.delayStrategy = () => 50;
326
325
  options.retry.retries = 2;
327
326
  return rpRetry(options)
328
- .then(() => {
329
- })
330
327
  .catch(() => {
331
- sinon.assert.notCalled(loggerSpyWarn);
328
+ sinon.assert.called(loggerSpyWarn);
332
329
  });
333
330
  });
334
331
  it('should generate a timeout error', () => {
@@ -336,13 +333,26 @@ describe('request-retry Unit Tests', () => {
336
333
  options.retry.timeout = 10;
337
334
  options.retry.retries = 2;
338
335
  return rpRetry(options)
339
- .then(() => {
340
- })
341
336
  .catch((err) => {
342
337
  expect(err.name).to.equal('System');
343
338
  expect(VError.cause(err).name).to.equal('TimeoutError');
344
- sinon.assert.notCalled(loggerSpyWarn);
339
+ // sinon.assert.called(loggerSpyWarn);
345
340
  });
346
341
  });
342
+ it('should stop mock server', () => rpRetry({
343
+ method: 'GET',
344
+ headers: {
345
+ 'x-correlation-id': correlationId,
346
+ },
347
+ url: 'http://localhost:9070/stop',
348
+ retry: {
349
+ delayStrategy: () => 100,
350
+ retries: 1,
351
+ },
352
+ })
353
+ .catch((err) => {
354
+ expect(err.name).to.equal('System');
355
+ expect(VError.cause(err).name).to.equal('TimeoutError');
356
+ }));
347
357
  });
348
358
  });
package/test/testEnv.js CHANGED
@@ -1,5 +1,21 @@
1
+ /* eslint no-process-env: "off" */
2
+
3
+ /**
4
+ * The following environment variables are set for the test:
5
+ *
6
+ * | Env variable name | Description | Default | Comments |
7
+ * | ----------------- | ----------- | ------- | -------- |
8
+ * | SUMO_LOGIC_ENDPOINT | endpoint to use to log on sumologic | null
9
+ * | SUMO_LOGIC_COLLECTOR_CODE | code to use to log on sumologic | null
10
+ * | NO_STACK | flag to have a stack associated with the log | yes
11
+ * | LOG_LEVEL | log level to log | error
12
+ * | CONSOLE_LEVEL | log level to diplay | debug
13
+ */
14
+
15
+ // process.env.SUMO_LOGIC_ENDPOINT = 'http://localhost:9080/logs/';
16
+ // process.env.SUMO_LOGIC_COLLECTOR_CODE = '1234';
1
17
  process.env.SUMO_LOGIC_ENDPOINT = null;
2
18
  process.env.SUMO_LOGIC_COLLECTOR_CODE = null;
3
19
  process.env.NO_STACK = 'yes';
4
- process.env.LOG_LEVEL = 'error';
5
- // process.env.CONSOLE_LEVEL = 'debug';
20
+ process.env.LOG_LEVEL = 'debug';
21
+ process.env.CONSOLE_LEVEL = 'debug';
package/Gulpfile.js DELETED
@@ -1,42 +0,0 @@
1
- /* eslint-disable import/no-extraneous-dependencies */
2
- const fs = require('fs');
3
- const log = require('fancy-log');
4
- const gulp = require('gulp');
5
- const eslint = require('gulp-eslint');
6
- const git = require('gulp-git');
7
- const jsdoc2md = require('jsdoc-to-markdown');
8
- const mocha = require('gulp-spawn-mocha');
9
-
10
- const files = [
11
- 'index.js',
12
- 'Gulpfile.js',
13
- 'test/**.js',
14
- 'lib/**.js',
15
- ];
16
-
17
- const createDocs = (done) => {
18
- jsdoc2md.render({ files: 'index.js' })
19
- .then((output) => fs.writeFileSync('README.md', output))
20
- .catch((err) => log.error('docs creation failed:', err.message));
21
- return done();
22
- };
23
-
24
- const lint = () => gulp.src(files)
25
- .pipe(eslint({}))
26
- .pipe(eslint.format())
27
- .pipe(eslint.failOnError());
28
-
29
- const add = () => gulp.src('README.md')
30
- .pipe(git.add({ quiet: true }));
31
-
32
- const test = () => gulp.src(['test/*.spec.js'])
33
- .pipe(mocha({
34
- reporter: 'mochawesome',
35
- exit: true,
36
- }));
37
-
38
- const docs = gulp.series(createDocs, add);
39
-
40
- gulp.task('lint', lint);
41
- gulp.task('docs', docs);
42
- gulp.task('test', test);
package/package.json.bak DELETED
@@ -1,60 +0,0 @@
1
- {
2
- "name": "@mimik/request-retry",
3
- "version": "1.0.1",
4
- "description": "Request retry wrapping request-promise",
5
- "main": "index.js",
6
- "scripts": {
7
- "lint": "gulp lint",
8
- "docs": "gulp docs",
9
- "test": "nyc gulp test",
10
- "prepublishOnly": "gulp docs; gulp lint; npm run test",
11
- "commit-ready": "gulp docs; gulp lint; npm run test"
12
- },
13
- "husky": {
14
- "hooks": {
15
- "pre-commit": "npm run commit-ready",
16
- "pre-push": "npm run test"
17
- }
18
- },
19
- "keywords": [
20
- "mimik",
21
- "microservice"
22
- ],
23
- "author": "mimik",
24
- "license": "Apache-2.0",
25
- "repository": {
26
- "type": "git",
27
- "url": "https://bitbucket.org/mimiktech/request-retry"
28
- },
29
- "dependencies": {
30
- "@mimik/response-helper": "2.2.1",
31
- "@mimik/sumologic-winston-logger": "1.3.0",
32
- "bluebird": "3.7.1",
33
- "request": "2.88.0",
34
- "request-promise": "4.2.5"
35
- },
36
- "devDependencies": {
37
- "acorn": "7.1.0",
38
- "body-parser": "1.19.0",
39
- "chai": "4.2.0",
40
- "eslint": "6.6.0",
41
- "eslint-config-airbnb": "18.0.1",
42
- "eslint-plugin-import": "2.18.2",
43
- "eslint-plugin-jsx-a11y": "6.2.3",
44
- "eslint-plugin-react": "7.16.0",
45
- "eslint-plugin-react-hooks": "2.2.0",
46
- "express": "4.17.1",
47
- "fancy-log": "1.3.3",
48
- "gulp": "4.0.2",
49
- "gulp-eslint": "6.0.0",
50
- "gulp-git": "2.9.0",
51
- "gulp-spawn-mocha": "6.0.0",
52
- "husky": "3.0.9",
53
- "jsdoc-to-markdown": "5.0.2",
54
- "mocha": "6.2.2",
55
- "mochawesome": "4.1.0",
56
- "nyc": "14.1.1",
57
- "sinon": "7.5.0",
58
- "verror": "1.10.0"
59
- }
60
- }