mirror of
https://github.com/valitydev/checkout.git
synced 2024-11-06 10:35:20 +00:00
TD-768: Restore fetch with retry (#245)
This commit is contained in:
parent
77151894df
commit
ec028279f7
@ -86,9 +86,16 @@ describe('fetch capi', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should catch json reject', async () => {
|
test('should retry json reject', async () => {
|
||||||
const errorMsg = 'Read json error';
|
const errorMsg = 'Read json error';
|
||||||
const mockFetchJson = jest.fn().mockRejectedValueOnce(errorMsg);
|
const expected = {
|
||||||
|
someField: 'someValue',
|
||||||
|
};
|
||||||
|
const mockFetchJson = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValueOnce(errorMsg)
|
||||||
|
.mockRejectedValueOnce(errorMsg)
|
||||||
|
.mockResolvedValueOnce(expected);
|
||||||
const mockFetch = jest.fn().mockResolvedValue({
|
const mockFetch = jest.fn().mockResolvedValue({
|
||||||
status: 200,
|
status: 200,
|
||||||
ok: true,
|
ok: true,
|
||||||
@ -99,10 +106,69 @@ describe('fetch capi', () => {
|
|||||||
const endpoint = 'https://api.test.com/endpoint';
|
const endpoint = 'https://api.test.com/endpoint';
|
||||||
const accessToken = 'testToken';
|
const accessToken = 'testToken';
|
||||||
|
|
||||||
|
const retryDelay = 50;
|
||||||
|
const retryLimit = 10;
|
||||||
|
|
||||||
|
const result = await fetchCapi({ endpoint, accessToken }, retryDelay, retryLimit);
|
||||||
|
expect(result).toStrictEqual(expected);
|
||||||
|
expect(mockFetchJson).toHaveBeenCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should retry failed fetch requests', async () => {
|
||||||
|
const expected = {
|
||||||
|
someField: 'someValue',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockFetch = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValueOnce(new Error('TypeError: Failed to fetch'))
|
||||||
|
.mockRejectedValueOnce(new Error('TypeError: Failed to fetch'))
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
status: 200,
|
||||||
|
ok: true,
|
||||||
|
json: async () => expected,
|
||||||
|
});
|
||||||
|
global.fetch = mockFetch;
|
||||||
|
|
||||||
|
const endpoint = 'https://api.test.com/endpoint';
|
||||||
|
const accessToken = 'testToken';
|
||||||
|
|
||||||
|
const retryDelay = 50;
|
||||||
|
const retryLimit = 10;
|
||||||
|
|
||||||
|
const result = await fetchCapi({ endpoint, accessToken }, retryDelay, retryLimit);
|
||||||
|
|
||||||
|
expect(result).toStrictEqual(expected);
|
||||||
|
expect(mockFetch).toHaveBeenCalledTimes(3);
|
||||||
|
const requestInit = {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
|
'X-Request-ID': expect.any(String),
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
};
|
||||||
|
expect(mockFetch).toHaveBeenCalledWith(endpoint, requestInit);
|
||||||
|
expect(mockFetch).toHaveBeenCalledWith(endpoint, requestInit);
|
||||||
|
expect(mockFetch).toHaveBeenCalledWith(endpoint, requestInit);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should retry failed fetch requests based on config', async () => {
|
||||||
|
const expectedError = new Error('TypeError: Failed to fetch');
|
||||||
|
const mockFetch = jest.fn().mockRejectedValue(expectedError);
|
||||||
|
global.fetch = mockFetch;
|
||||||
|
|
||||||
|
const endpoint = 'https://api.test.com/endpoint';
|
||||||
|
const accessToken = 'testToken';
|
||||||
|
|
||||||
|
const retryDelay = 50;
|
||||||
|
const retryLimit = 10;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fetchCapi({ endpoint, accessToken });
|
await fetchCapi({ endpoint, accessToken }, retryDelay, retryLimit);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(error).toStrictEqual(errorMsg);
|
expect(error).toEqual(expectedError);
|
||||||
}
|
}
|
||||||
|
expect(mockFetch).toHaveBeenCalledTimes(retryLimit);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import delay from 'checkout/utils/delay';
|
||||||
import guid from 'checkout/utils/guid';
|
import guid from 'checkout/utils/guid';
|
||||||
|
|
||||||
export type FetchCapiParams = {
|
export type FetchCapiParams = {
|
||||||
@ -15,8 +16,10 @@ const getDetails = async (response: Response) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const provideResponse = async (response: Response) => {
|
const provideResponse = async (response: Response, retryDelay: number, retryLimit: number, attempt: number = 0) => {
|
||||||
|
try {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
attempt++;
|
||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
@ -24,10 +27,19 @@ const provideResponse = async (response: Response) => {
|
|||||||
statusText: response.statusText || undefined,
|
statusText: response.statusText || undefined,
|
||||||
details: await getDetails(response),
|
details: await getDetails(response),
|
||||||
});
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
if (attempt === retryLimit) {
|
||||||
|
return Promise.reject(ex);
|
||||||
|
}
|
||||||
|
await delay(retryDelay);
|
||||||
|
return provideResponse(response, retryDelay, retryLimit, attempt);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const doFetch = async (param: FetchCapiParams) =>
|
const doFetch = async (param: FetchCapiParams, retryDelay: number, retryLimit: number, attempt: number = 0) => {
|
||||||
fetch(param.endpoint, {
|
try {
|
||||||
|
attempt++;
|
||||||
|
return await fetch(param.endpoint, {
|
||||||
method: param.method || 'GET',
|
method: param.method || 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=utf-8',
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
@ -36,8 +48,16 @@ const doFetch = async (param: FetchCapiParams) =>
|
|||||||
},
|
},
|
||||||
body: param.body ? JSON.stringify(param.body) : undefined,
|
body: param.body ? JSON.stringify(param.body) : undefined,
|
||||||
});
|
});
|
||||||
|
} catch (ex) {
|
||||||
export const fetchCapi = async <T>(param: FetchCapiParams): Promise<T> => {
|
if (attempt === retryLimit) {
|
||||||
const response = await doFetch(param);
|
return Promise.reject(ex);
|
||||||
return await provideResponse(response);
|
}
|
||||||
|
await delay(retryDelay);
|
||||||
|
return doFetch(param, retryDelay, retryLimit, attempt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchCapi = async <T>(param: FetchCapiParams, retryDelay = 3000, retryLimit = 10): Promise<T> => {
|
||||||
|
const response = await doFetch(param, retryDelay, retryLimit);
|
||||||
|
return await provideResponse(response, retryDelay, retryLimit);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user