From 74bc8222d62b06936e33ebf5e80e10958796db72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20=C5=81=C4=85giewka?= Date: Mon, 30 Dec 2024 10:46:53 +0100 Subject: [PATCH] Feature/Refactored `got` calls to use `AbortSignal.timeout()` without `AbortController()` (#4153) * Feature/refactor got calls to use AbortSignal.timeout Instead of manually creating AbortController and controlling the abort with setTimeout. Feature available since node v16.14.0 and v17.3.0[^1] and is built to replace the exact scenario that all these requests have. [^1]:https://nodejs.org/docs/latest-v22.x/api/globals.html#static-method-abortsignaltimeoutdelay * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> --- CHANGELOG.md | 1 + apps/api/src/app/info/info.service.ts | 40 ++++++------------- apps/api/src/app/logo/logo.service.ts | 10 ++--- .../coingecko/coingecko.service.ts | 36 ++++------------- .../openfigi/openfigi.service.ts | 8 +--- .../trackinsight/trackinsight.service.ts | 38 +++++------------- .../eod-historical-data.service.ts | 34 +++------------- .../financial-modeling-prep.service.ts | 26 +++--------- .../ghostfolio/ghostfolio.service.ts | 34 +++------------- .../data-provider/manual/manual.service.ts | 10 ++--- .../rapid-api/rapid-api.service.ts | 10 ++--- 11 files changed, 58 insertions(+), 189 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0cfcbadc..0fe75a2fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved support for automatic deletion of unused asset profiles when deleting activities - Migrated the coupon redemption to the notification service for prompt dialogs +- Refactored `got` calls to use `AbortSignal.timeout()` without `AbortController()` - Improved the language localization for German (`de`) - Eliminated `body-parser` in favor of using `@nestjs/platform-express` - Upgraded the _Stripe_ dependencies diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 904a97090..cd4035164 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -155,18 +155,14 @@ export class InfoService { private async countDockerHubPulls(): Promise { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const { pull_count } = await got( `https://hub.docker.com/v2/repositories/ghostfolio/ghostfolio`, { headers: { 'User-Agent': 'request' }, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).json(); @@ -180,15 +176,11 @@ export class InfoService { private async countGitHubContributors(): Promise { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const { body } = await got('https://github.com/ghostfolio/ghostfolio', { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) }); const $ = cheerio.load(body); @@ -207,18 +199,14 @@ export class InfoService { private async countGitHubStargazers(): Promise { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const { stargazers_count } = await got( `https://api.github.com/repos/ghostfolio/ghostfolio`, { headers: { 'User-Agent': 'request' }, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).json(); @@ -335,12 +323,6 @@ export class InfoService { PROPERTY_BETTER_UPTIME_MONITOR_ID )) as string; - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const { data } = await got( `https://uptime.betterstack.com/api/v2/monitors/${monitorId}/sla?from=${format( subDays(new Date(), 90), @@ -353,7 +335,9 @@ export class InfoService { )}` }, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).json(); diff --git a/apps/api/src/app/logo/logo.service.ts b/apps/api/src/app/logo/logo.service.ts index 908921a19..759db2afb 100644 --- a/apps/api/src/app/logo/logo.service.ts +++ b/apps/api/src/app/logo/logo.service.ts @@ -44,18 +44,14 @@ export class LogoService { } private getBuffer(aUrl: string) { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - return got( `https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${aUrl}&size=64`, { headers: { 'User-Agent': 'request' }, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).buffer(); } diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index 991980161..ddc04ffcd 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -69,16 +69,12 @@ export class CoinGeckoService implements DataProviderInterface { }; try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const { name } = await got(`${this.apiUrl}/coins/${symbol}`, { headers: this.headers, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) }).json(); response.name = name; @@ -118,12 +114,6 @@ export class CoinGeckoService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const { prices } = await got( `${ this.apiUrl @@ -133,7 +123,7 @@ export class CoinGeckoService implements DataProviderInterface { { headers: this.headers, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -179,12 +169,6 @@ export class CoinGeckoService implements DataProviderInterface { } try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const quotes = await got( `${this.apiUrl}/simple/price?ids=${symbols.join( ',' @@ -192,7 +176,7 @@ export class CoinGeckoService implements DataProviderInterface { { headers: this.headers, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -228,16 +212,12 @@ export class CoinGeckoService implements DataProviderInterface { let items: LookupItem[] = []; try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const { coins } = await got(`${this.apiUrl}/search?query=${query}`, { headers: this.headers, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) }).json(); items = coins.map(({ id: symbol, name }) => { diff --git a/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts b/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts index 8d2d180e3..3efe7e40d 100644 --- a/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/openfigi/openfigi.service.ts @@ -43,18 +43,12 @@ export class OpenFigiDataEnhancerService implements DataEnhancerInterface { this.configurationService.get('API_KEY_OPEN_FIGI'); } - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const mappings = await got .post(`${OpenFigiDataEnhancerService.baseUrl}/v3/mapping`, { headers, json: [{ exchCode: exchange, idType: 'TICKER', idValue: ticker }], // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) }) .json(); diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index 437ef4eba..a4e695284 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -45,34 +45,24 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { return response; } - let abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const profile = await got( `${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol}.json`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ) .json() .catch(() => { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - return got( `${TrackinsightDataEnhancerService.baseUrl}/funds/${ symbol.split('.')?.[0] }.json`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ) .json() @@ -87,34 +77,26 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { response.isin = isin; } - abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const holdings = await got( `${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol}.json`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ) .json() .catch(() => { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - return got( `${TrackinsightDataEnhancerService.baseUrl}/holdings/${ symbol.split('.')?.[0] }.json`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ) .json() diff --git a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index 1c5696daa..3ba8eb04f 100644 --- a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts +++ b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts @@ -91,16 +91,10 @@ export class EodHistoricalDataService implements DataProviderInterface { } try { - const abortController = new AbortController(); - const response: { [date: string]: IDataProviderHistoricalResponse; } = {}; - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const historicalResult = await got( `${this.URL}/div/${symbol}?api_token=${ this.apiKey @@ -110,7 +104,7 @@ export class EodHistoricalDataService implements DataProviderInterface { )}`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -146,12 +140,6 @@ export class EodHistoricalDataService implements DataProviderInterface { symbol = this.convertToEodSymbol(symbol); try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const response = await got( `${this.URL}/eod/${symbol}?api_token=${ this.apiKey @@ -161,7 +149,7 @@ export class EodHistoricalDataService implements DataProviderInterface { )}&period=${granularity}`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -217,19 +205,13 @@ export class EodHistoricalDataService implements DataProviderInterface { }); try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const realTimeResponse = await got( `${this.URL}/real-time/${eodHistoricalDataSymbols[0]}?api_token=${ this.apiKey }&fmt=json&s=${eodHistoricalDataSymbols.join(',')}`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -418,17 +400,13 @@ export class EodHistoricalDataService implements DataProviderInterface { })[] = []; try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const response = await got( `${this.URL}/search/${aQuery}?api_token=${this.apiKey}`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).json(); diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index a1774fc09..933af4802 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -72,17 +72,11 @@ export class FinancialModelingPrepService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const { historical } = await got( `${this.URL}/historical-price-full/${symbol}?apikey=${this.apiKey}`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -130,17 +124,11 @@ export class FinancialModelingPrepService implements DataProviderInterface { } try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const quotes = await got( `${this.URL}/quote/${symbols.join(',')}?apikey=${this.apiKey}`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -176,17 +164,13 @@ export class FinancialModelingPrepService implements DataProviderInterface { let items: LookupItem[] = []; try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const result = await got( `${this.URL}/search?query=${query}&apikey=${this.apiKey}`, { // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).json(); diff --git a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts index 7102176ae..b6e86949b 100644 --- a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts @@ -86,12 +86,6 @@ export class GhostfolioService implements DataProviderInterface { } = {}; try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const { dividends } = await got( `${this.URL}/v2/data-providers/ghostfolio/dividends/${symbol}?from=${format(from, DATE_FORMAT)}&granularity=${granularity}&to=${format( to, @@ -100,7 +94,7 @@ export class GhostfolioService implements DataProviderInterface { { headers: await this.getRequestHeaders(), // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -136,12 +130,6 @@ export class GhostfolioService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const { historicalData } = await got( `${this.URL}/v2/data-providers/ghostfolio/historical/${symbol}?from=${format(from, DATE_FORMAT)}&granularity=${granularity}&to=${format( to, @@ -150,7 +138,7 @@ export class GhostfolioService implements DataProviderInterface { { headers: await this.getRequestHeaders(), // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -204,18 +192,12 @@ export class GhostfolioService implements DataProviderInterface { } try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, requestTimeout); - const { quotes } = await got( `${this.URL}/v2/data-providers/ghostfolio/quotes?symbols=${symbols.join(',')}`, { headers: await this.getRequestHeaders(), // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout(requestTimeout) } ).json(); @@ -253,18 +235,14 @@ export class GhostfolioService implements DataProviderInterface { let searchResult: LookupResponse = { items: [] }; try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - searchResult = await got( `${this.URL}/v2/data-providers/ghostfolio/lookup?query=${query}`, { headers: await this.getRequestHeaders(), // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).json(); } catch (error) { diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts index 6f1c56f15..3e98a9d77 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -275,17 +275,13 @@ export class ManualService implements DataProviderInterface { scraperConfiguration: ScraperConfiguration ): Promise { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - let locale = scraperConfiguration.locale; const { body, headers } = await got(scraperConfiguration.url, { headers: scraperConfiguration.headers as Headers, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) }); if (headers['content-type'].includes('application/json')) { diff --git a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts index e7abc0ff6..08cc2ef05 100644 --- a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts +++ b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts @@ -135,12 +135,6 @@ export class RapidApiService implements DataProviderInterface { oneYearAgo: { value: number; valueText: string }; }> { try { - const abortController = new AbortController(); - - setTimeout(() => { - abortController.abort(); - }, this.configurationService.get('REQUEST_TIMEOUT')); - const { fgi } = await got( `https://fear-and-greed-index.p.rapidapi.com/v1/fgi`, { @@ -150,7 +144,9 @@ export class RapidApiService implements DataProviderInterface { 'x-rapidapi-key': this.configurationService.get('API_KEY_RAPID_API') }, // @ts-ignore - signal: abortController.signal + signal: AbortSignal.timeout( + this.configurationService.get('REQUEST_TIMEOUT') + ) } ).json();