1 | import { noop } from './utils.js';
|
2 | const delay = (ms) => new Promise((resolve) => {
|
3 | setTimeout(resolve, ms);
|
4 | });
|
5 | const defaultWait = (attempts) => {
|
6 | return Math.pow(Math.SQRT2, attempts);
|
7 | };
|
8 | export default function rateLimit(instance, maxRetry = 5) {
|
9 | const { responseLogger = noop, requestLogger = noop } = instance.defaults;
|
10 | instance.interceptors.request.use(function (config) {
|
11 | requestLogger(config);
|
12 | return config;
|
13 | }, function (error) {
|
14 | requestLogger(error);
|
15 | return Promise.reject(error);
|
16 | });
|
17 | instance.interceptors.response.use(function (response) {
|
18 |
|
19 | responseLogger(response);
|
20 | return response;
|
21 | }, async function (error) {
|
22 | const { response } = error;
|
23 | const { config } = error;
|
24 | responseLogger(error);
|
25 |
|
26 | if (!config || !instance.defaults.retryOnError) {
|
27 | return Promise.reject(error);
|
28 | }
|
29 |
|
30 | const doneAttempts = config.attempts || 1;
|
31 | if (doneAttempts > maxRetry) {
|
32 | error.attempts = config.attempts;
|
33 | return Promise.reject(error);
|
34 | }
|
35 | let retryErrorType = null;
|
36 | let wait = defaultWait(doneAttempts);
|
37 |
|
38 | if (!response) {
|
39 | retryErrorType = 'Connection';
|
40 | }
|
41 | else if (response.status >= 500 && response.status < 600) {
|
42 |
|
43 | retryErrorType = `Server ${response.status}`;
|
44 | }
|
45 | else if (response.status === 429) {
|
46 |
|
47 | retryErrorType = 'Rate limit';
|
48 |
|
49 | if (response.headers && error.response.headers['x-contentful-ratelimit-reset']) {
|
50 | wait = response.headers['x-contentful-ratelimit-reset'];
|
51 | }
|
52 | }
|
53 | if (retryErrorType) {
|
54 |
|
55 | wait = Math.floor(wait * 1000 + Math.random() * 200 + 500);
|
56 | instance.defaults.logHandler('warning', `${retryErrorType} error occurred. Waiting for ${wait} ms before retrying...`);
|
57 |
|
58 | config.attempts = doneAttempts + 1;
|
59 | |
60 |
|
61 |
|
62 | delete config.httpAgent;
|
63 | delete config.httpsAgent;
|
64 | return delay(wait).then(() => instance(config));
|
65 | }
|
66 | return Promise.reject(error);
|
67 | });
|
68 | }
|