diff --git a/CHANGELOG.md b/CHANGELOG.md index 87b326a5c..9e72eaa0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the error handling in data providers - Upgraded `yahoo-finance2` from version `3.4.1` to `3.6.4` ## 2.192.0 - 2025-08-21 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 7776ff46c..8a3adb507 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -78,7 +78,7 @@ export class CoinGeckoService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { + if (['AbortError', 'TimeoutError'].includes(error?.name)) { message = `RequestError: The operation to get the asset profile for ${symbol} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; @@ -196,8 +196,10 @@ export class CoinGeckoService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { - message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( + if (['AbortError', 'TimeoutError'].includes(error?.name)) { + message = `RequestError: The operation to get the quotes for ${symbols.join( + ', ' + )} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; } @@ -237,7 +239,7 @@ export class CoinGeckoService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { + if (['AbortError', 'TimeoutError'].includes(error?.name)) { 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/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index ddb94bb1a..d06071ac3 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 @@ -282,8 +282,10 @@ export class EodHistoricalDataService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { - message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( + if (['AbortError', 'TimeoutError'].includes(error?.name)) { + message = `RequestError: The operation to get the quotes for ${symbols.join( + ', ' + )} was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; } @@ -426,7 +428,7 @@ export class EodHistoricalDataService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { + 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 ${( 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 2dcb689a7..ed2aa5f25 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 @@ -202,7 +202,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { + if (['AbortError', 'TimeoutError'].includes(error?.name)) { message = `RequestError: The operation to get the asset profile for ${symbol} was aborted because the request to the data provider took more than ${( requestTimeout / 1000 ).toFixed(3)} seconds`; @@ -392,8 +392,10 @@ export class FinancialModelingPrepService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { - message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( + if (['AbortError', 'TimeoutError'].includes(error?.name)) { + message = `RequestError: The operation to get the quotes for ${symbols.join( + ', ' + )} was aborted because the request to the data provider took more than ${( requestTimeout / 1000 ).toFixed(3)} seconds`; } @@ -469,7 +471,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { + if (['AbortError', 'TimeoutError'].includes(error?.name)) { 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/ghostfolio/ghostfolio.service.ts b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts index 3fd9e1bea..48ba42bd4 100644 --- a/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/services/data-provider/ghostfolio/ghostfolio.service.ts @@ -68,14 +68,16 @@ export class GhostfolioService implements DataProviderInterface { } catch (error) { let message = error; - if (error.name === 'AbortError') { - message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( + if (['AbortError', 'TimeoutError'].includes(error?.name)) { + message = `RequestError: The operation to get the asset profile for ${symbol} was aborted because the request to the data provider took more than ${( requestTimeout / 1000 ).toFixed(3)} seconds`; - } else if (error.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS) { + } else if ( + error?.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS + ) { message = 'RequestError: The daily request limit has been exceeded'; - } else if (error.response?.statusCode === StatusCodes.UNAUTHORIZED) { - if (!error.request?.options?.headers?.authorization?.includes('-')) { + } else if (error?.response?.statusCode === StatusCodes.UNAUTHORIZED) { + if (!error?.request?.options?.headers?.authorization?.includes('-')) { message = 'RequestError: The provided API key is invalid. Please update it in the Settings section of the Admin Control panel.'; } else { @@ -229,14 +231,18 @@ export class GhostfolioService implements DataProviderInterface { } catch (error) { let message = error; - if (error.name === 'AbortError') { - message = `RequestError: The operation to get the quotes was aborted because the request to the data provider took more than ${( + if (['AbortError', 'TimeoutError'].includes(error?.name)) { + message = `RequestError: The operation to get the quotes for ${symbols.join( + ', ' + )} was aborted because the request to the data provider took more than ${( requestTimeout / 1000 ).toFixed(3)} seconds`; - } else if (error.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS) { + } else if ( + error?.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS + ) { message = 'RequestError: The daily request limit has been exceeded'; - } else if (error.response?.statusCode === StatusCodes.UNAUTHORIZED) { - if (!error.request?.options?.headers?.authorization?.includes('-')) { + } else if (error?.response?.statusCode === StatusCodes.UNAUTHORIZED) { + if (!error?.request?.options?.headers?.authorization?.includes('-')) { message = 'RequestError: The provided API key is invalid. Please update it in the Settings section of the Admin Control panel.'; } else { @@ -272,14 +278,16 @@ export class GhostfolioService implements DataProviderInterface { } catch (error) { let message = error; - if (error.name === 'AbortError') { + if (['AbortError', 'TimeoutError'].includes(error?.name)) { message = `RequestError: The operation to search for ${query} was aborted because the request to the data provider took more than ${( requestTimeout / 1000 ).toFixed(3)} seconds`; - } else if (error.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS) { + } else if ( + error?.response?.statusCode === StatusCodes.TOO_MANY_REQUESTS + ) { message = 'RequestError: The daily request limit has been exceeded'; } else if (error.response?.statusCode === StatusCodes.UNAUTHORIZED) { - if (!error.request?.options?.headers?.authorization?.includes('-')) { + if (!error?.request?.options?.headers?.authorization?.includes('-')) { message = 'RequestError: The provided API key is invalid. Please update it in the Settings section of the Admin Control panel.'; } else { 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 5675f1eb0..62b3ed71c 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 @@ -165,7 +165,7 @@ export class RapidApiService implements DataProviderInterface { } catch (error) { let message = error; - if (error?.name === 'AbortError') { + if (['AbortError', 'TimeoutError'].includes(error?.name)) { message = `RequestError: The operation was aborted because the request to the data provider took more than ${( this.configurationService.get('REQUEST_TIMEOUT') / 1000 ).toFixed(3)} seconds`; diff --git a/libs/common/src/lib/personal-finance-tools.ts b/libs/common/src/lib/personal-finance-tools.ts index e814ac415..d0e47ee3b 100644 --- a/libs/common/src/lib/personal-finance-tools.ts +++ b/libs/common/src/lib/personal-finance-tools.ts @@ -446,6 +446,17 @@ export const personalFinanceTools: Product[] = [ origin: 'Pakistan', slogan: 'Advanced portfolio tracking and stock market information' }, + { + founded: 2021, + hasFreePlan: true, + hasSelfHostingAbility: true, + key: 'invmon', + name: 'InvMon', + origin: 'Switzerland', + pricingPerYear: '$156', + slogan: 'Track all your assets, investments and portfolios in one place', + useAnonymously: true + }, { founded: 2011, hasFreePlan: true,