diff --git a/CHANGELOG.md b/CHANGELOG.md index da274d2ff..c756f4e6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Reused the request timeout in various functions of the data providers - Refactored the _ZEN_ page to standalone - Upgraded `chart.js` from version `4.4.9` to `4.5.0` +### Fixed + +- Handled an exception in the get quotes functionality of the _Financial Modeling Prep_ service + ## 2.194.0 - 2025-08-27 ### Added 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 8a3adb507..561d7d6db 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -214,15 +214,16 @@ export class CoinGeckoService implements DataProviderInterface { return 'bitcoin'; } - public async search({ query }: GetSearchParams): Promise { + public async search({ + query, + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT') + }: GetSearchParams): Promise { let items: LookupItem[] = []; try { const { coins } = await fetch(`${this.apiUrl}/search?query=${query}`, { headers: this.headers, - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) }).then((res) => res.json()); items = coins.map(({ id: symbol, name }) => { 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 58b4d0d3e..c18ec193f 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 @@ -51,9 +51,13 @@ export class EodHistoricalDataService implements DataProviderInterface { } public async getAssetProfile({ + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), symbol }: GetAssetProfileParams): Promise> { - const [searchResult] = await this.getSearchResult(symbol); + const [searchResult] = await this.getSearchResult({ + requestTimeout, + query: symbol + }); if (!searchResult) { return undefined; @@ -304,8 +308,11 @@ export class EodHistoricalDataService implements DataProviderInterface { return 'AAPL.US'; } - public async search({ query }: GetSearchParams): Promise { - const searchResult = await this.getSearchResult(query); + public async search({ + query, + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT') + }: GetSearchParams): Promise { + const searchResult = await this.getSearchResult({ query, requestTimeout }); return { items: searchResult @@ -394,7 +401,13 @@ export class EodHistoricalDataService implements DataProviderInterface { return name; } - private async getSearchResult(aQuery: string) { + private async getSearchResult({ + query, + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT') + }: { + query: string; + requestTimeout?: number; + }) { let searchResult: (LookupItem & { assetClass: AssetClass; assetSubClass: AssetSubClass; @@ -403,11 +416,9 @@ export class EodHistoricalDataService implements DataProviderInterface { try { const response = await fetch( - `${this.URL}/search/${aQuery}?api_token=${this.apiKey}`, + `${this.URL}/search/${query}?api_token=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.json()); @@ -433,7 +444,7 @@ export class EodHistoricalDataService implements DataProviderInterface { let message = error; if (['AbortError', 'TimeoutError'].includes(error?.name)) { - message = `RequestError: The operation to search for ${aQuery} was aborted because the request to the data provider took more than ${( + message = `RequestError: The operation to search for ${query} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; } 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 d1e591d5d..8f52fb779 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 @@ -365,8 +365,13 @@ export class FinancialModelingPrepService implements DataProviderInterface { await Promise.all( quotes.map(({ symbol }) => { - return this.getAssetProfile({ symbol }).then(({ currency }) => { - currencyBySymbolMap[symbol] = { currency }; + return this.getAssetProfile({ + requestTimeout, + symbol + }).then((assetProfile) => { + if (assetProfile?.currency) { + currencyBySymbolMap[symbol] = { currency: assetProfile.currency }; + } }); }) ); @@ -411,7 +416,10 @@ export class FinancialModelingPrepService implements DataProviderInterface { return 'AAPL'; } - public async search({ query }: GetSearchParams): Promise { + public async search({ + query, + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT') + }: GetSearchParams): Promise { const assetProfileBySymbolMap: { [symbol: string]: Partial; } = {}; @@ -422,9 +430,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { const result = await fetch( `${this.getUrl({ version: 'stable' })}/search-isin?isin=${query.toUpperCase()}&apikey=${this.apiKey}`, { - signal: AbortSignal.timeout( - this.configurationService.get('REQUEST_TIMEOUT') - ) + signal: AbortSignal.timeout(requestTimeout) } ).then((res) => res.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 c4a6996c1..ca8d72827 100644 --- a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts @@ -284,8 +284,8 @@ export class GhostfolioService implements DataProviderInterface { } public async search({ - requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), - query + query, + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT') }: GetSearchParams): Promise { let searchResult: LookupResponse = { items: [] };