From 5e5846abde34a9f27a269a8fc646fb45b30fe477 Mon Sep 17 00:00:00 2001 From: xenticore Date: Wed, 30 Apr 2025 18:31:33 -0400 Subject: [PATCH] refactor: rewrite async promises as chain --- __tests__/priceService.test.ts | 5 +- src/content/content.ts | 107 ++++++++++++-------------------- src/content/priceService.ts | 11 ++-- src/content/pricing/pricestf.ts | 42 ++++++------- 4 files changed, 67 insertions(+), 98 deletions(-) diff --git a/__tests__/priceService.test.ts b/__tests__/priceService.test.ts index 2ee36f9..b66c64d 100644 --- a/__tests__/priceService.test.ts +++ b/__tests__/priceService.test.ts @@ -69,7 +69,7 @@ describe('Price Service', () => { }) test('fetchPrice rejects with 401 when no token provided', async () => { - await expect(fetchPrice('', mockDefIndex + ";" + mockQuality)).rejects.toBe(401) + await expect(fetchPrice('', mockDefIndex + ";" + mockQuality)).rejects.toThrow() }) test('fetchPrice handles pricing API errors', async () => { @@ -77,7 +77,8 @@ describe('Price Service', () => { (priceUsingPricesTF as jest.Mock).mockRejectedValue(testError); (getStorageValue as jest.Mock).mockResolvedValue(null) - await expect(fetchPrice(mockToken, mockDefIndex + ";" + mockQuality)).rejects.toBe(testError) + await expect(fetchPrice(mockToken, mockDefIndex + ";" + mockQuality)).rejects.toThrow() + }) }) test('fetchKeyPrice uses correct parameters', async () => { diff --git a/src/content/content.ts b/src/content/content.ts index abcd48a..8629543 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -227,21 +227,17 @@ async function inject() { // Check item schema for Australium variant of current defindex if(itemSchema[itemIndex].hasAustraliumVariant) { - promises.push(new Promise(async (resolve) => { - logDebug(`Fetching price for Australium ${itemName}`) - let data: ItemPriceData | null - try { - data = await fetchPrice(token, `${itemIndex};11;australium`, currentTime); - updateTime = new Date(data.update) - } catch { - log(`Australium ${itemName} is unpriced or unavailable, skipping...`) - } + promises.push(fetchPrice(token, `${itemIndex};11;australium`, currentTime).then(data => { + updateTime = new Date(data.update) + log(`Saving price for Australium ${itemName}`) const priceRow = createPriceRow($T("Australium"), data, keyPrice, exchangeRates, locale, "https://wiki.teamfortress.com/wiki/Australium_weapons") priceRows.push({order: 99, row: priceRow, category: PriceRowCategory.None}) - resolve() - return + }) + .catch((error) => { + logError(error) + log(`Australium ${itemName} is unpriced or unavailable, skipping...`) })) } @@ -259,37 +255,29 @@ async function inject() { festiveHeadingRow.style.display = 'none'; festiveHeadingRow.appendChild(festiveHeading); - promises.push(new Promise(async (resolve) => { - logDebug(`Fetching price for Festive ${itemName}`) - let data: ItemPriceData | null - try { - data = await fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};6`, currentTime); - updateTime = new Date(data.update) - } catch { - log(`Festive ${itemName} is unpriced or unavailable, skipping...`) - } + promises.push(fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};6`, currentTime).then(data => { + updateTime = new Date(data.update) + log(`updateTime price for Festive ${itemName}`) const priceRow = createPriceRow($T("Unique"), data, keyPrice, exchangeRates, locale) priceRows.push({order: -1, row: priceRow, category: PriceRowCategory.Festive}) - resolve() - return + }) + .catch((error) => { + logError(error) + log(`Festive ${itemName} is unpriced or unavailable, skipping...`) })) - promises.push(new Promise(async (resolve) => { - logDebug(`Fetching price for Strange Festive ${itemName}`) - let data: ItemPriceData | null - try { - data = await fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};11`, currentTime); - updateTime = new Date(data.update) - } catch { - log(`Strange Festive ${itemName} is unpriced or unavailable, skipping...`) - } + promises.push(fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};11`, currentTime).then(data => { + updateTime = new Date(data.update) + log(`Saving price for Strange Festive ${itemName}`) const priceRow = createPriceRow($T("Strange"), data, keyPrice, exchangeRates, locale) priceRows.push({order: 11, row: priceRow, category: PriceRowCategory.Festive}) - resolve() - return + }) + .catch((error) => { + logError(error) + log(`Strange Festive ${itemName} is unpriced or unavailable, skipping...`) })) } @@ -310,30 +298,23 @@ async function inject() { killstreakKitHeadingRow.style.display = 'none'; killstreakKitHeadingRow.appendChild(heading); [1,2,3].map((tier) => { - promises.push(new Promise(async (resolve) => { - logDebug(`Fetching price for ${itemName} Killstreak Kit Tier ${tier}`) - let data: ItemPriceData | null - try { - let kitIndex: number - switch (tier) { - default: - case 1: kitIndex = 6527; break; - case 2: kitIndex = 6523; break; - case 3: kitIndex = 6526; break; - } - data = await fetchPrice(token, `${kitIndex};6;uncraftable;kt-${tier};td-${itemIndex}`, currentTime); - updateTime = new Date(data.update) - } catch { - log(`${itemName} Killstreak Kit Tier ${tier} is unpriced or unavailable, skipping...`) - resolve() - return - } + let kitIndex: number + switch (tier) { + default: + case 1: kitIndex = 6527; break; + case 2: kitIndex = 6523; break; + case 3: kitIndex = 6526; break; + } + promises.push(fetchPrice(token, `${kitIndex};6;uncraftable;kt-${tier};td-${itemIndex}`, currentTime).then(data => { + updateTime = new Date(data.update) + logDebug(`Saving price for ${itemName} Killstreak Kit Tier ${tier}`) const priceRow = createPriceRow($T(`kt-${tier}`), data, keyPrice, exchangeRates, locale, "https://wiki.teamfortress.com/wiki/Killstreak_Kit") priceRows.push({order: tier, row: priceRow, category: PriceRowCategory.KillstreakKit}) - resolve() - return + }) + .catch((error) => { + logError(`Failed to fetch price for ${itemName} Killstreak Kit Tier ${tier}`, error) })) }) } @@ -366,23 +347,17 @@ async function inject() { const itemName = itemSchema[variantIndex].name // FIXME: variantName should match wiki display name const variantName = itemName.includes('Mk.II') ? itemName.split(' ')[0] + ' Mk.II' : itemName.split(' ')[0] - promises.push(new Promise(async (resolve) => { - logDebug(`Fetching price for ${itemName}`) - let data: ItemPriceData | null - try { - data = await fetchPrice(token, `${variantIndex};11`, currentTime); - updateTime = new Date(data.update) - } catch { - log(`${itemName} is unpriced or unavailable, skipping...`) - } + promises.push(fetchPrice(token, `${variantIndex};11`, currentTime).then(data => { + logDebug(`Saving price for ${itemName}`) + updateTime = new Date(data.update) const priceRow = createPriceRow($T(variantName), data, keyPrice, exchangeRates, locale, "https://wiki.teamfortress.com/wiki/Botkiller_weapons") - // FIXME: order should be by release - // Silver Mk.I, Gold Mk.II, Rust, Blood, Carbonado, Diamond, Silver Mk.II, Gold Mk.II priceRows.push({order: botkillerOrder.indexOf(variantName), row: priceRow, category: PriceRowCategory.Botkiller}) - resolve() - return + }) + .catch((error) => { + logError(error) + log(`Strange Festive ${itemName} is unpriced or unavailable, skipping...`) })) }) } diff --git a/src/content/priceService.ts b/src/content/priceService.ts index a7fa811..efa0a1a 100644 --- a/src/content/priceService.ts +++ b/src/content/priceService.ts @@ -1,7 +1,7 @@ import { defindex_key, storage_priceprefix } from "./config" import { priceUsingPricesTF } from "./pricing/pricestf" import { getStorageValue, setStorageValue } from "./storage" -import { logDebug, log } from "./utils/log" +import { logDebug } from "./utils/log" /** Pricing data for a given TF2 item. */ export class ItemPriceData { @@ -40,7 +40,6 @@ export async function fetchKeyPrice(token: string) { * @param ttl Time to cache results in milliseconds. 30 minutes by default. */ export async function fetchPrice(token: string, sku: string, update: Date = new Date(), ttl: number = 30 * 60 * 1000): Promise { - return new Promise(async (resolve, reject) => { let data: ItemPriceData | null const cached: ItemPriceData = await getStorageValue(storage_priceprefix + sku, null) @@ -51,7 +50,7 @@ export async function fetchPrice(token: string, sku: string, update: Date = new if (!data || data.sku != sku || 'update' in data && 'ttl' in data && Date.now() > (new Date(data.update).getTime() + data.ttl)) { logDebug(`Fetching price data for ${sku}`) if(!token) { - reject(401) + throw new Error('No token provided') } data = new ItemPriceData() data.sku = sku @@ -65,8 +64,7 @@ export async function fetchPrice(token: string, sku: string, update: Date = new data.metal = response.metal } } catch (error) { - log(`Received ${error} error while pricing ${sku} using prices.tf`) - reject(error) + throw new Error(`Received ${error} error while pricing ${sku} using prices.tf`) } if ('metal' in data && 'keys' in data) { @@ -75,6 +73,5 @@ export async function fetchPrice(token: string, sku: string, update: Date = new } else { logDebug(`Using cached price data for ${sku}`) } - resolve(data) - }) + return data } diff --git a/src/content/pricing/pricestf.ts b/src/content/pricing/pricestf.ts index 55e56da..f125ec3 100644 --- a/src/content/pricing/pricestf.ts +++ b/src/content/pricing/pricestf.ts @@ -42,49 +42,45 @@ async function priceUsingPricesTF(token: string, sku: string, retries: number = // prices.tf // https://api2.prices.tf/prices/${sku} // Authorization: Bearer ${token} - return GM_fetch(`https://api2.prices.tf/prices/${encodeURIComponent(sku)}`, { - method: 'get', - headers: { - 'Accept': 'application/json', - 'Authorization': `Bearer ${token}`, - } - }) - .then(response => { - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } + try { + const response = await GM_fetch(`https://api2.prices.tf/prices/${encodeURIComponent(sku)}`, { + method: 'get', + headers: { + 'Accept': 'application/json', + 'Authorization': `Bearer ${token}`, + } + }) if (response.status === 404 && sku.includes(';') && !sku.includes(';uncraftable')) { const quality: number = parseInt(sku.split(';')[1], 10); if(quality === 6) { // Try uncraftable variant if unique weapon - return priceUsingPricesTF(token, sku + ';uncraftable', retries); + return priceUsingPricesTF(token, sku + ';uncraftable'); } } - if(response.status === 404) { - throw new Error(`Item not found: ${sku}`); - } if(response.status === 503) { // Happens if we send too many requests in a short period of time // Retry after a few seconds - if(retries > 0) { - logDebug(`Cloudflare rate limit exceeded, trying again after 2 seconds, ${retries} retries left`) + if(retries >= 0) { + logDebug(`Cloudflare rate limit exceeded, trying again after 1 second, ${retries} retries left`) + await new Promise(resolve => setTimeout(resolve, 1000)); return priceUsingPricesTF(token, sku, retries - 1); } else { throw new Error(`Cloudflare rate limit exceeded, stopping`) } } - return response.json(); - }) - .then(data => { + if (!response.ok) { + throw new Error(`Pricing request for ${sku} failed with status code: ${response.status}`); + } + const data = await response.json(); const prices = new PricesResponse(); prices.keys = data['sellKeys'] prices.metal = data['sellHalfScrap'] / 18.0; return prices; - }) - .catch(error => { + } + catch(error) { logError(`Failed to fetch prices from prices.tf for item ${sku}`) throw error; - }) + } } export { getPricesToken, priceUsingPricesTF, PricesResponse } \ No newline at end of file