Comparing version 1.0.0 to 1.0.1
{ | ||
"name": "phin-retry", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "The ultra-lightweight Node.js HTTP client", | ||
@@ -10,3 +10,4 @@ "main": "src/index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "uvu tests", | ||
"coverage": "c8 --include=src npm test && c8 report --reporter=text-lcov > coverage/lcov.info" | ||
}, | ||
@@ -41,3 +42,8 @@ "repository": { | ||
"node": ">=8" | ||
}, | ||
"devDependencies": { | ||
"c8": "^7.2.1", | ||
"pactum": "^1.0.26", | ||
"uvu": "^0.3.1" | ||
} | ||
} | ||
} |
# phin-retry | ||
![Build](https://github.com/ASaiAnudeep/phin-retry/workflows/Build/badge.svg?branch=master) | ||
![Coverage](https://img.shields.io/codeclimate/coverage/ASaiAnudeep/phin-retry) | ||
The ultra-lightweight Node.js HTTP client. | ||
@@ -19,29 +22,62 @@ | ||
// should be used in async context | ||
const post1 = await request.get('https://jsonplaceholder.typicode.com/posts/1'); | ||
const response = await request.get('https://jsonplaceholder.typicode.com/posts/1'); | ||
const post2 = await request.get({ | ||
url: 'https://jsonplaceholder.typicode.com/posts', | ||
qs: { | ||
id: 1 | ||
}, | ||
await request.post({ | ||
url: 'http://localhost:9393/api/post', | ||
body: { msg: 'input' }, | ||
retry: 3, | ||
delay: 500 | ||
}); | ||
// custom retry, error & delay strategy | ||
const response = await request.delete({ | ||
url: 'http://localhost:9393/api/delete', | ||
auth: { | ||
user: 'name', | ||
pass: 'secret' | ||
}, | ||
errorStrategy: (response, error, options) => { | ||
if (error) return true; | ||
if (response.statusCode >= 400) { | ||
return false; | ||
} | ||
return true; | ||
}, | ||
retryStrategy: (response, error, options) => { | ||
if (error) return true; | ||
if (options.method === 'POST') return false; | ||
if (response.statusCode >=200 && response.StatusCode < 300) { | ||
return false; | ||
} | ||
return true; | ||
}, | ||
delayStrategy: (response, error, options, delay) => { | ||
if (error) return 5000; | ||
return 2000; | ||
}, | ||
}); | ||
``` | ||
All options from **phin** are supported. Refer [Phin](https://www.npmjs.com/package/phin) for more usage examples. | ||
* It supports **get**, **post**, **put**, **delete**, **patch** HTTP methods. | ||
* By default, this library will retry once on failure (StatusCode >= 500 & network errors) with a delay of 100 or 1000 milliseconds. Override this behaviour with custom retry strategy function. | ||
* Responses with status codes < 200 & >= 300 are thrown as errors. Override this behaviour with custom error strategy function. | ||
* All options from **phin** are supported. Refer [Phin](https://www.npmjs.com/package/phin) for more usage examples. | ||
## API | ||
### Methods | ||
### Defaults | ||
| Method | Description | Usage | | ||
| -------- | ------------------------------------------ | ---------------------------- | | ||
| `get` | performs a GET request on the resource | `await request.get({})` | | ||
| `post` | performs a POST request on the resource | `await request.post({})` | | ||
| `put` | performs a PUT request on the resource | `await request.put({})` | | ||
| `delete` | performs a DELETE request on the resource | `await request.delete({})` | | ||
| `patch` | performs a PATCH request on the resource | `await request.patch({})` | | ||
| `head` | performs a HEAD request on the resource | `await request.head({})` | | ||
Access default options through `request.defaults`. | ||
| Option | Type | Description | | ||
| ---------------- | -------- | ---------------------------------- | | ||
| `retry` | number | max no of times to retry (1) | | ||
| `delay` | number | delay between retries (100ms) | | ||
| `networkErrorDelay` | any | delay for network errors (1000ms) | | ||
| `retryStrategy` | function | default retry strategy function | | ||
| `delayStrategy` | function | default delay strategy function | | ||
| `errorStrategy` | function | default error strategy function | | ||
### Options | ||
@@ -51,12 +87,14 @@ | ||
| Method | Type | Description | | ||
| ---------------- | -------- | --------------------------- | | ||
| `url` | string | request url | | ||
| `qs` | object | query parameters | | ||
| `auth` | object | authentication object | | ||
| `retry` | number | max no of times to retry | | ||
| `delay` | number | delay between retries | | ||
| `body` | any | equivalent to data in phin | | ||
| `fullResponse` | boolean | returns full phin response | | ||
**By default, this library will retry once on failure (StatusCode < 200 & >= 300 ) with a delay of 100 milliseconds.** | ||
| Method | Type | Description | | ||
| ---------------- | -------- | ------------------------------- | | ||
| `url` | string | request url | | ||
| `qs` | object | query parameters | | ||
| `auth` | object | authentication object | | ||
| `headers` | object | headers object | | ||
| `retry` | number | max no of times to retry | | ||
| `delay` | number | delay between retries | | ||
| `body` | any | equivalent to data in phin | | ||
| `fullResponse` | boolean | returns full phin response | | ||
| `retryStrategy` | function | custom retry strategy function | | ||
| `delayStrategy` | function | custom delay strategy function | | ||
| `errorStrategy` | function | custom error strategy function | |
167
src/index.js
@@ -7,2 +7,5 @@ const phin = require('phin'); | ||
* @property {number} [delay] - delay in ms between retries - defaults to 100ms | ||
* @property {function} [retryStrategy] - custom retry strategy function | ||
* @property {function} [delayStrategy] - custom delay strategy function | ||
* @property {function} [errorStrategy] - custom error strategy function | ||
* @property {object} [qs] - key-value pairs of query parameters | ||
@@ -21,12 +24,55 @@ * @property {object} [auth] - auth object | ||
class StatusCodeError extends Error { | ||
constructor(response) { | ||
super(response.statusCode + `${response.statusMessage ? ` - ${response.statusMessage}` : ''}`); | ||
constructor(response, fullResponse) { | ||
const message = response.statusMessage ? response.statusMessag : ''; | ||
super(`${response.statusCode} - ${message}`); | ||
this.name = this.constructor.name; | ||
this.statusCode = response.statusCode; | ||
this.statusMessage = response.statusMessage; | ||
this.body = helper.json(response.body); | ||
if (fullResponse) { | ||
this.response = response; | ||
} | ||
} | ||
} | ||
const DEFAULT_RETRY = 1; | ||
const DEFAULT_DELAY = 100; | ||
const strategies = { | ||
retry(response, error) { | ||
if (error) { | ||
return true; | ||
} | ||
if (response.statusCode >= 500) { | ||
return true; | ||
} | ||
return false; | ||
}, | ||
delay(response, error, options, delay) { | ||
if (error && delay === defaults.delay) { | ||
return defaults.networkErrorDelay; | ||
} | ||
return delay; | ||
}, | ||
error(response, error) { | ||
if (error) { | ||
return true; | ||
} | ||
if (response.statusCode < 200 || response.statusCode >= 300) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
}; | ||
const defaults = { | ||
retry: 1, | ||
delay: 100, | ||
networkErrorDelay: 1000, | ||
retryStrategy: strategies.retry, | ||
delayStrategy: strategies.delay, | ||
errorStrategy: strategies.error | ||
}; | ||
const helper = { | ||
@@ -41,5 +87,4 @@ | ||
return buffer.toString(); | ||
} else { | ||
return buffer; | ||
} | ||
return buffer; | ||
}, | ||
@@ -57,21 +102,21 @@ | ||
retry(options) { | ||
if (typeof options.retry === 'number') { | ||
return options.retry; | ||
} else if (typeof process.env.PHIN_RETRY === 'number') { | ||
return process.env.PHIN_RETRY; | ||
} else { | ||
return DEFAULT_RETRY; | ||
} | ||
return typeof options.retry === 'number' ? options.retry : defaults.retry; | ||
}, | ||
delay(options) { | ||
if (typeof options.delay === 'number') { | ||
return options.delay; | ||
} else if (typeof process.env.PHIN_DELAY === 'number') { | ||
return process.env.PHIN_DELAY; | ||
} else { | ||
return DEFAULT_DELAY; | ||
} | ||
return typeof options.delay === 'number' ? options.delay : defaults.delay; | ||
}, | ||
retryStrategy(options) { | ||
return typeof options.retryStrategy === 'function' ? options.retryStrategy : defaults.retryStrategy; | ||
}, | ||
delayStrategy(options) { | ||
return typeof options.delayStrategy === 'function' ? options.delayStrategy : defaults.delayStrategy; | ||
}, | ||
errorStrategy(options) { | ||
return typeof options.errorStrategy === 'function' ? options.errorStrategy : defaults.errorStrategy; | ||
}, | ||
qs(options) { | ||
@@ -113,2 +158,5 @@ if (options.qs && typeof options.qs === 'object') { | ||
delete options.body; | ||
delete options.retryStrategy; | ||
delete options.delayStrategy; | ||
delete options.errorStrategy; | ||
}, | ||
@@ -119,2 +167,5 @@ | ||
const delay = helper.delay(options); | ||
const retryStrategy = helper.retryStrategy(options); | ||
const delayStrategy = helper.delayStrategy(options); | ||
const errorStrategy = helper.errorStrategy(options); | ||
const fullResponse = options.fullResponse || false; | ||
@@ -125,3 +176,26 @@ helper.qs(options); | ||
helper.delete(options); | ||
return { retry, delay, fullResponse }; | ||
return { retry, delay, fullResponse, retryStrategy, delayStrategy, errorStrategy }; | ||
}, | ||
updateOptions(options, retry, delay, fullResponse, retryStrategy, errorStrategy) { | ||
options.retry = retry - 1; | ||
options.delay = delay; | ||
options.fullResponse = fullResponse; | ||
options.retryStrategy = retryStrategy; | ||
options.errorStrategy = errorStrategy; | ||
}, | ||
reject(response, error, full) { | ||
if (error) { | ||
throw error; | ||
} | ||
throw new StatusCodeError(response, full); | ||
}, | ||
resolve(response, full) { | ||
if (full) { | ||
return response; | ||
} else { | ||
return helper.json(response.body); | ||
} | ||
} | ||
@@ -133,2 +207,5 @@ | ||
defaults, | ||
phin, | ||
/** | ||
@@ -139,3 +216,3 @@ * @param {RequestOptions} options | ||
options.method = 'GET'; | ||
return this.__fetch( options); | ||
return this.__fetch(options); | ||
}, | ||
@@ -183,31 +260,21 @@ | ||
__fetch(opts) { | ||
async __fetch(opts) { | ||
const options = typeof opts === 'string' ? { url: opts, method: 'GET' } : opts; | ||
const { retry, delay, fullResponse } = helper.init(options); | ||
return phin(options) | ||
.then(async res => { | ||
if (res.statusCode < 200 || res.statusCode >= 300) { | ||
if (retry > 0) { | ||
options.retry = retry - 1; | ||
options.delay = delay; | ||
options.fullResponse = fullResponse; | ||
await helper.sleep(delay); | ||
return this[options.method.toLowerCase()](options); | ||
} else { | ||
const error = new StatusCodeError(res); | ||
if (fullResponse) { | ||
error.response = res; | ||
} else { | ||
error.body = helper.json(res.body); | ||
} | ||
return Promise.reject(error); | ||
} | ||
} else { | ||
if (fullResponse) { | ||
return Promise.resolve(res); | ||
} else { | ||
return Promise.resolve(helper.json(res.body)); | ||
} | ||
} | ||
}); | ||
const { retry, delay, fullResponse, retryStrategy, delayStrategy, errorStrategy } = helper.init(options); | ||
let res, err; | ||
try { | ||
res = await phin(options); | ||
} catch (error) { | ||
err = error; | ||
} | ||
if (retryStrategy(res, err, options) && retry > 0) { | ||
helper.updateOptions(options, retry, delay, fullResponse, retryStrategy, errorStrategy); | ||
await helper.sleep(delayStrategy(res, err, options, delay)); | ||
return this[options.method.toLowerCase()](options); | ||
} | ||
if (errorStrategy(res, err, options)) { | ||
return helper.reject(res, err, fullResponse); | ||
} else { | ||
return helper.resolve(res, fullResponse); | ||
} | ||
} | ||
@@ -214,0 +281,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
12838
235
0
99
0
3