1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-21 17:37:01 +01:00

fix(20316): enrich error message for fetch-with-cache (#20742)

This commit is contained in:
Danica Shen 2023-09-06 15:02:22 +01:00 committed by GitHub
parent 0f938c5dc8
commit 38876dc450
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 164 additions and 115 deletions

View File

@ -172,11 +172,12 @@ export default class SwapsController {
}
async fetchSwapsNetworkConfig(chainId) {
const response = await fetchWithCache(
getBaseApi('network', chainId),
{ method: 'GET' },
{ cacheRefreshTime: 600000 },
);
const response = await fetchWithCache({
url: getBaseApi('network', chainId),
fetchOptions: { method: 'GET' },
cacheOptions: { cacheRefreshTime: 600000 },
functionName: 'fetchSwapsNetworkConfig',
});
const { refreshRates, parameters = {} } = response || {};
if (
!refreshRates ||

View File

@ -21,9 +21,11 @@ describe('Fetch with cache', () => {
.get('/price')
.reply(200, '{"average": 1}');
const response = await fetchWithCache(
'https://fetchwithcache.metamask.io/price',
);
const response = await fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
functionName: 'fetchPrice',
});
expect(response).toStrictEqual({
average: 1,
});
@ -39,9 +41,10 @@ describe('Fetch with cache', () => {
cachedTime: Date.now(),
});
const response = await fetchWithCache(
'https://fetchwithcache.metamask.io/price',
);
const response = await fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
functionName: 'fetchPrice',
});
expect(response).toStrictEqual({
average: 1,
});
@ -57,11 +60,11 @@ describe('Fetch with cache', () => {
cachedTime: Date.now() - 1000,
});
const response = await fetchWithCache(
'https://fetchwithcache.metamask.io/price',
{},
{ cacheRefreshTime: 123 },
);
const response = await fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
cacheOptions: { cacheRefreshTime: 123 },
functionName: 'fetchPrice',
});
expect(response).toStrictEqual({
average: 3,
});
@ -74,11 +77,11 @@ describe('Fetch with cache', () => {
.reply(200, '{"average": 4}');
await expect(() =>
fetchWithCache(
'https://fetchwithcache.metamask.io/price',
{},
{ timeout: 20 },
),
fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
cacheOptions: { timeout: 20 },
functionName: 'fetchPrice',
}),
).rejects.toThrow({
name: 'AbortError',
message: 'The user aborted a request.',
@ -91,7 +94,10 @@ describe('Fetch with cache', () => {
.reply(500, '{"average": 6}');
await expect(() =>
fetchWithCache('https://fetchwithcache.metamask.io/price'),
fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
functionName: 'fetchPrice',
}),
).rejects.toThrow('');
});
@ -101,8 +107,10 @@ describe('Fetch with cache', () => {
.reply(200, '{"average": 7}');
await expect(() =>
fetchWithCache('https://fetchwithcache.metamask.io/price', {
method: 'POST',
fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
fetchOptions: { method: 'POST' },
functionName: 'fetchPrice',
}),
).rejects.toThrow('');
});
@ -113,7 +121,11 @@ describe('Fetch with cache', () => {
.reply(200, '{"average": 8}');
await expect(() =>
fetchWithCache('https://fetchwithcache.metamask.io/price', { body: 1 }),
fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
fetchOptions: { body: 1 },
functionName: 'fetchPrice',
}),
).rejects.toThrow('');
});
@ -123,8 +135,10 @@ describe('Fetch with cache', () => {
.reply(200, '{"average": 9}');
await expect(() =>
fetchWithCache('https://fetchwithcache.metamask.io/price', {
headers: { 'Content-Type': 'text/plain' },
fetchWithCache({
url: 'https://fetchwithcache.metamask.io/price',
fetchOptions: { headers: { 'Content-Type': 'text/plain' } },
functionName: 'fetchPrice',
}),
).rejects.toThrow({
message: 'fetchWithCache only supports JSON responses',
@ -147,16 +161,16 @@ describe('Fetch with cache', () => {
});
await Promise.all([
fetchWithCache(
'https://fetchwithcache.metamask.io/foo',
{},
{ cacheRefreshTime: 123 },
),
fetchWithCache(
'https://fetchwithcache.metamask.io/bar',
{},
{ cacheRefreshTime: 123 },
),
fetchWithCache({
url: 'https://fetchwithcache.metamask.io/foo',
cacheOptions: { cacheRefreshTime: 123 },
functionName: 'fetchFoo',
}),
fetchWithCache({
url: 'https://fetchwithcache.metamask.io/bar',
cacheOptions: { cacheRefreshTime: 123 },
functionName: 'fetchFoo',
}),
]);
expect(

View File

@ -2,11 +2,17 @@ import { MINUTE, SECOND } from '../constants/time';
import getFetchWithTimeout from '../modules/fetch-with-timeout';
import { getStorageItem, setStorageItem } from './storage-helpers';
const fetchWithCache = async (
const fetchWithCache = async ({
url,
fetchOptions = {},
{ cacheRefreshTime = MINUTE * 6, timeout = SECOND * 30 } = {},
) => {
cacheOptions: { cacheRefreshTime = MINUTE * 6, timeout = SECOND * 30 } = {},
functionName = '',
}: {
url: string;
fetchOptions?: Record<string, any>;
cacheOptions?: Record<string, any>;
functionName: string;
}) => {
if (
fetchOptions.body ||
(fetchOptions.method && fetchOptions.method !== 'GET')
@ -40,7 +46,7 @@ const fetchWithCache = async (
});
if (!response.ok) {
throw new Error(
`Fetch failed with status '${response.status}': '${response.statusText}'`,
`Fetch with cache failed within function ${functionName} with status'${response.status}': '${response.statusText}'`,
);
}
const responseJson = await response.json();

View File

@ -274,11 +274,12 @@ export async function fetchTradesInfo(
const queryString = new URLSearchParams(urlParams).toString();
const tradeURL = `${getBaseApi('trade', chainId)}${queryString}`;
const tradesResponse = await fetchWithCache(
tradeURL,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: 0, timeout: SECOND * 15 },
);
const tradesResponse = await fetchWithCache({
url: tradeURL,
fetchOptions: { method: 'GET', headers: clientIdHeader },
cacheOptions: { cacheRefreshTime: 0, timeout: SECOND * 15 },
functionName: 'fetchTradesInfo',
});
const newQuotes = tradesResponse.reduce((aggIdTradeMap, quote) => {
if (
quote.trade &&

View File

@ -38,8 +38,10 @@ export default function TransactionDecoding({ to = '', inputData: data = '' }) {
(async () => {
setLoading(true);
try {
const networks = await fetchWithCache(FETCH_SUPPORTED_NETWORKS_URI, {
method: 'GET',
const networks = await fetchWithCache({
url: FETCH_SUPPORTED_NETWORKS_URI,
fetchOptions: { method: 'GET' },
functionName: 'fetchSupportedNetworks',
});
if (
@ -57,7 +59,11 @@ export default function TransactionDecoding({ to = '', inputData: data = '' }) {
'network-id': network,
})}`;
const response = await fetchWithCache(requestUrl, { method: 'GET' });
const response = await fetchWithCache({
url: requestUrl,
fetchOptions: { method: 'GET' },
functionName: 'fetchProject',
});
const { info: projectInfo, fetchedVia, address } = response;

View File

@ -49,9 +49,10 @@ const NewNetworkInfo = () => {
};
const getIsTokenDetectionSupported = async () => {
const fetchedTokenData = await fetchWithCache(
`${TOKEN_API_METASWAP_CODEFI_URL}${providerConfig.chainId}`,
);
const fetchedTokenData = await fetchWithCache({
url: `${TOKEN_API_METASWAP_CODEFI_URL}${providerConfig.chainId}`,
functionName: 'getIsTokenDetectionSupported',
});
return !fetchedTokenData.error;
};

View File

@ -33,9 +33,10 @@ describe('NewNetworkInfo', () => {
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
);
const updateTokenDetectionSupportStatus = await fetchWithCache(
'https://token-api.metaswap.codefi.network/tokens/0x1',
);
const updateTokenDetectionSupportStatus = await fetchWithCache({
url: 'https://token-api.metaswap.codefi.network/tokens/0x1',
functionName: 'getTokenDetectionSupportStatus',
});
const store = configureMockStore()(
state,
@ -54,9 +55,10 @@ describe('NewNetworkInfo', () => {
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
);
const updateTokenDetectionSupportStatus = await fetchWithCache(
'https://token-api.metaswap.codefi.network/tokens/0x1',
);
const updateTokenDetectionSupportStatus = await fetchWithCache({
url: 'https://token-api.metaswap.codefi.network/tokens/0x1',
functionName: 'getTokenDetectionSupportStatus',
});
state.metamask.nativeCurrency = '';
@ -77,10 +79,10 @@ describe('NewNetworkInfo', () => {
200,
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
);
const updateTokenDetectionSupportStatus = await fetchWithCache(
'https://token-api.metaswap.codefi.network/tokens/0x1',
);
const updateTokenDetectionSupportStatus = await fetchWithCache({
url: 'https://token-api.metaswap.codefi.network/tokens/0x1',
functionName: 'getTokenDetectionSupportStatus',
});
const store = configureMockStore()(
state,
@ -99,9 +101,10 @@ describe('NewNetworkInfo', () => {
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
);
const updateTokenDetectionSupportStatus = await fetchWithCache(
'https://token-api.metaswap.codefi.network/tokens/0x1',
);
const updateTokenDetectionSupportStatus = await fetchWithCache({
url: 'https://token-api.metaswap.codefi.network/tokens/0x1',
functionName: 'getTokenDetectionSupportStatus',
});
const store = configureMockStore()(
state,
@ -117,9 +120,10 @@ describe('NewNetworkInfo', () => {
.get('/tokens/0x3')
.reply(200, '{"error":"ChainId 0x3 is not supported"}');
const updateTokenDetectionSupportStatus = await fetchWithCache(
'https://token-api.metaswap.codefi.network/tokens/0x3',
);
const updateTokenDetectionSupportStatus = await fetchWithCache({
url: 'https://token-api.metaswap.codefi.network/tokens/0x3',
functionName: 'getTokenDetectionSupportStatus',
});
const store = configureMockStore()(
state,
@ -135,9 +139,10 @@ describe('NewNetworkInfo', () => {
.get('/tokens/0x3')
.reply(200, '{"error":"ChainId 0x3 is not supported"}');
const updateTokenDetectionSupportStatus = await fetchWithCache(
'https://token-api.metaswap.codefi.network/tokens/0x3',
);
const updateTokenDetectionSupportStatus = await fetchWithCache({
url: 'https://token-api.metaswap.codefi.network/tokens/0x3',
functionName: 'getTokenDetectionSupportStatus',
});
state.metamask.providerConfig.ticker = null;
@ -156,9 +161,10 @@ describe('NewNetworkInfo', () => {
.get('/tokens/0x3')
.reply(200, '{"error":"ChainId 0x3 is not supported"}');
const updateTokenDetectionSupportStatus = await fetchWithCache(
'https://token-api.metaswap.codefi.network/tokens/0x3',
);
const updateTokenDetectionSupportStatus = await fetchWithCache({
url: 'https://token-api.metaswap.codefi.network/tokens/0x3',
functionName: 'getTokenDetectionSupportStatus',
});
const store = configureMockStore()(
state,

View File

@ -26,15 +26,16 @@ import fetchWithCache from '../../../shared/lib/fetch-with-cache';
*/
async function getMethodFrom4Byte(fourBytePrefix) {
const fourByteResponse = await fetchWithCache(
`https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`,
{
const fourByteResponse = await fetchWithCache({
url: `https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`,
fetchOptions: {
referrerPolicy: 'no-referrer-when-downgrade',
body: null,
method: 'GET',
mode: 'cors',
},
);
functionName: 'getMethodFrom4Byte',
});
fourByteResponse.results.sort((a, b) => {
return new Date(a.created_at).getTime() < new Date(b.created_at).getTime()
? -1

View File

@ -134,7 +134,10 @@ const ERROR_CONNECTING_TO_RPC = {
async function getAlerts(pendingApproval) {
const alerts = [];
const safeChainsList =
(await fetchWithCache('https://chainid.network/chains.json')) || [];
(await fetchWithCache({
url: 'https://chainid.network/chains.json',
functionName: 'getSafeChainsList',
})) || [];
const matchedChain = safeChainsList.find(
(chain) =>
chain.chainId === parseInt(pendingApproval.requestData.chainId, 16),

View File

@ -356,7 +356,10 @@ const NetworksForm = ({
try {
safeChainsList =
(await fetchWithCache('https://chainid.network/chains.json')) || [];
(await fetchWithCache({
url: 'https://chainid.network/chains.json',
functionName: 'getSafeChainsList',
})) || [];
} catch (err) {
log.warn('Failed to fetch the chainList from chainid.network', err);
providerError = err;

View File

@ -117,11 +117,12 @@ export async function fetchToken(
chainId: any,
): Promise<Json> {
const tokenUrl = getBaseApi('token', chainId);
return await fetchWithCache(
`${tokenUrl}?address=${contractAddress}`,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
);
return await fetchWithCache({
url: `${tokenUrl}?address=${contractAddress}`,
fetchOptions: { method: 'GET', headers: clientIdHeader },
cacheOptions: { cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
functionName: 'fetchToken',
});
}
type Token = { symbol: string; address: string };
@ -129,11 +130,12 @@ export async function fetchTokens(
chainId: keyof typeof SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
): Promise<SwapsTokenObject[]> {
const tokensUrl = getBaseApi('tokens', chainId);
const tokens = await fetchWithCache(
tokensUrl,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
);
const tokens = await fetchWithCache({
url: tokensUrl,
fetchOptions: { method: 'GET', headers: clientIdHeader },
cacheOptions: { cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
functionName: 'fetchTokens',
});
const logError = false;
const tokenObject = SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId] || null;
return [
@ -152,11 +154,12 @@ export async function fetchTokens(
export async function fetchAggregatorMetadata(chainId: any): Promise<object> {
const aggregatorMetadataUrl = getBaseApi('aggregatorMetadata', chainId);
const aggregators = await fetchWithCache(
aggregatorMetadataUrl,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
);
const aggregators = await fetchWithCache({
url: aggregatorMetadataUrl,
fetchOptions: { method: 'GET', headers: clientIdHeader },
cacheOptions: { cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
functionName: 'fetchAggregatorMetadata',
});
const filteredAggregators = {} as any;
for (const aggKey in aggregators) {
if (
@ -175,11 +178,12 @@ export async function fetchAggregatorMetadata(chainId: any): Promise<object> {
export async function fetchTopAssets(chainId: any): Promise<object> {
const topAssetsUrl = getBaseApi('topAssets', chainId);
const response =
(await fetchWithCache(
topAssetsUrl,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
)) || [];
(await fetchWithCache({
url: topAssetsUrl,
functionName: 'fetchTopAssets',
fetchOptions: { method: 'GET', headers: clientIdHeader },
cacheOptions: { cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
})) || [];
const topAssetsMap = response.reduce(
(_topAssetsMap: any, asset: { address: string }, index: number) => {
if (validateData(TOP_ASSET_VALIDATORS, asset, topAssetsUrl)) {
@ -196,21 +200,23 @@ export async function fetchSwapsFeatureFlags(): Promise<any> {
const v2ApiBaseUrl = process.env.SWAPS_USE_DEV_APIS
? SWAPS_DEV_API_V2_BASE_URL
: SWAPS_API_V2_BASE_URL;
return await fetchWithCache(
`${v2ApiBaseUrl}/featureFlags`,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: 600000 },
);
return await fetchWithCache({
url: `${v2ApiBaseUrl}/featureFlags`,
fetchOptions: { method: 'GET', headers: clientIdHeader },
cacheOptions: { cacheRefreshTime: 600000 },
functionName: 'fetchSwapsFeatureFlags',
});
}
export async function fetchTokenPrice(address: string): Promise<any> {
const query = `contract_addresses=${address}&vs_currencies=eth`;
const prices = await fetchWithCache(
`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`,
{ method: 'GET' },
{ cacheRefreshTime: 60000 },
);
const prices = await fetchWithCache({
url: `https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`,
fetchOptions: { method: 'GET' },
cacheOptions: { cacheRefreshTime: 60000 },
functionName: 'fetchTokenPrice',
});
return prices?.[address]?.eth;
}
@ -223,11 +229,12 @@ export async function fetchSwapsGasPrices(chainId: any): Promise<
}
> {
const gasPricesUrl = getBaseApi('gasPrices', chainId);
const response = await fetchWithCache(
gasPricesUrl,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: 30000 },
);
const response = await fetchWithCache({
url: gasPricesUrl,
fetchOptions: { method: 'GET', headers: clientIdHeader },
cacheOptions: { cacheRefreshTime: 30000 },
functionName: 'fetchSwapsGasPrices',
});
const responseIsValid = validateData(
SWAP_GAS_PRICE_VALIDATOR,
response,