From b0f770e50af6db9563be747d9d7e34f88267bd18 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 22 Aug 2025 20:08:28 +0200 Subject: [PATCH] Feature/improve error handling in data providers (#5387) * Improve error handling * Update changelog --- CHANGELOG.md | 4 +++ .../coingecko/coingecko.service.ts | 10 +++--- .../eod-historical-data.service.ts | 8 +++-- .../financial-modeling-prep.service.ts | 10 +++--- .../ghostfolio/ghostfolio.service.ts | 34 ++++++++++++------- .../rapid-api/rapid-api.service.ts | 2 +- 6 files changed, 43 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 196fb1853..0344f8d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Extended the data providers management of the admin control panel by every data provider in use +### Changed + +- Improved the error handling in data providers + ## 2.192.0 - 2025-08-21 ### 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 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`;