From 5a6dbac82c618fbb3827b216b6643ec7997e1539 Mon Sep 17 00:00:00 2001 From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Thu, 5 Nov 2026 01:38:57 +0700 Subject: [PATCH] Task/improve type safety in exchange rate and portfolio table services (#8391) Improve type safety --- .../exchange-rate-data.service.ts | 33 +++++++++++-------- .../portfolio-table.service.spec.ts | 14 +++++++- .../portfolio-table.service.ts | 13 +++++--- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index f67748c007..d547de0233 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts @@ -88,7 +88,7 @@ export class ExchangeRateDataService { ); const lastDateString = dateStrings.reduce((a, b) => { return a > b ? a : b; - }, undefined); + }, ''); let previousExchangeRate = exchangeRatesByCurrency[`${currency}${targetCurrency}`]?.[ @@ -214,7 +214,7 @@ export class ExchangeRateDataService { const resultExtended = result; for (const symbol of Object.keys(result)) { - const [currency1, currency2] = symbol.match(/.{1,3}/g); + const [currency1, currency2] = symbol.match(/.{1,3}/g) ?? []; const [date] = Object.keys(result[symbol]); // Calculate the opposite direction @@ -226,7 +226,7 @@ export class ExchangeRateDataService { } for (const symbol of Object.keys(resultExtended)) { - const [currency1, currency2] = symbol.match(/.{1,3}/g); + const [currency1, currency2] = symbol.match(/.{1,3}/g) ?? []; this.exchangeRates[symbol] = resultExtended[symbol]?.[dateStringOfYesterday]?.marketPrice; @@ -302,7 +302,7 @@ export class ExchangeRateDataService { const derivedCurrencyFactor = this.derivedCurrencyFactors[`${aFromCurrency}${aToCurrency}`]; - let factor: number; + let factor: number | undefined; if (aFromCurrency === aToCurrency) { factor = 1; @@ -324,8 +324,8 @@ export class ExchangeRateDataService { } else { // Calculate indirectly via base currency - let marketPriceBaseCurrencyFromCurrency: number; - let marketPriceBaseCurrencyToCurrency: number; + let marketPriceBaseCurrencyFromCurrency: number | undefined; + let marketPriceBaseCurrencyToCurrency: number | undefined; try { if (aFromCurrency === DEFAULT_CURRENCY) { @@ -337,7 +337,7 @@ export class ExchangeRateDataService { date: aDate, symbol: `${DEFAULT_CURRENCY}${aFromCurrency}` }) - )?.marketPrice as number; + )?.marketPrice; } } catch {} @@ -351,14 +351,19 @@ export class ExchangeRateDataService { date: aDate, symbol: `${DEFAULT_CURRENCY}${aToCurrency}` }) - )?.marketPrice as number; + )?.marketPrice; } } catch {} - // Calculate the opposite direction - factor = - (1 / marketPriceBaseCurrencyFromCurrency) * - marketPriceBaseCurrencyToCurrency; + if ( + isNumber(marketPriceBaseCurrencyFromCurrency) && + isNumber(marketPriceBaseCurrencyToCurrency) + ) { + // Calculate the opposite direction + factor = + (1 / marketPriceBaseCurrencyFromCurrency) * + marketPriceBaseCurrencyToCurrency; + } } } @@ -530,7 +535,9 @@ export class ExchangeRateDataService { } }) ).forEach(({ currency }) => { - currencies.push(currency); + if (currency) { + currencies.push(currency); + } }); ( diff --git a/apps/api/src/services/portfolio-table/portfolio-table.service.spec.ts b/apps/api/src/services/portfolio-table/portfolio-table.service.spec.ts index 4a213684b8..ecb09b2093 100644 --- a/apps/api/src/services/portfolio-table/portfolio-table.service.spec.ts +++ b/apps/api/src/services/portfolio-table/portfolio-table.service.spec.ts @@ -121,6 +121,7 @@ function createHolding({ assetSubClass = AssetSubClass.STOCK, dataSource = DataSource.YAHOO, isin, + name, symbol = 'AAPL' }: { allocationInPercentage?: number; @@ -128,6 +129,7 @@ function createHolding({ assetSubClass?: AssetSubClass; dataSource?: DataSource; isin?: string | null; + name?: string | null; symbol?: string; } = {}) { return { @@ -140,7 +142,7 @@ function createHolding({ isin, symbol, currency: 'CHF', - name: `Name of ${symbol}` + name: name === undefined ? `Name of ${symbol}` : name }, dateOfFirstActivity: new Date('2024-01-01'), grossPerformance: 100, @@ -631,6 +633,16 @@ describe('PortfolioTableService', () => { expect(row).toContain('| Name of AAPL | | AAPL |'); }); + it('gives an empty name if the asset profile has none', async () => { + const result = await getHoldingsTable([createHolding({ name: null })]); + + const [row] = result.split('\n').filter((line) => { + return line.includes('| AAPL |'); + }); + + expect(row).toContain('| | | AAPL |'); + }); + it('gives the translation of the asset class and of the asset sub class', async () => { const result = await getHoldingsTable([createHolding()]); diff --git a/apps/api/src/services/portfolio-table/portfolio-table.service.ts b/apps/api/src/services/portfolio-table/portfolio-table.service.ts index bcf2839fba..1f51bd5b9b 100644 --- a/apps/api/src/services/portfolio-table/portfolio-table.service.ts +++ b/apps/api/src/services/portfolio-table/portfolio-table.service.ts @@ -52,7 +52,10 @@ function getDataSourceColumnDefinition( const dataSource = getDataSource(row); return dataSource - ? transformDataSourceInResponse({ configurationService, dataSource }) + ? (transformDataSourceInResponse({ + configurationService, + dataSource + }) ?? '') : ''; }, name: DATA_SOURCE_COLUMN_NAME @@ -176,7 +179,7 @@ export class PortfolioTableService { }), { getValue: ({ assetProfile, currency }) => { - return currency ?? assetProfile.currency; + return currency ?? assetProfile.currency ?? ''; }, name: 'Currency' }, @@ -226,7 +229,7 @@ export class PortfolioTableService { [ { getValue: ({ assetProfile }) => { - return assetProfile.name; + return assetProfile.name ?? ''; }, name: 'Name' }, @@ -249,7 +252,7 @@ export class PortfolioTableService { }), { getValue: ({ assetProfile }) => { - return assetProfile.currency; + return assetProfile.currency ?? ''; }, name: 'Currency' }, @@ -696,7 +699,7 @@ export class PortfolioTableService { const performanceSection = ['## Performance', '']; - if (chart?.length > 0) { + if (chart && chart.length > 0) { performanceSection.push( await getMarkdownTable({ columnDefinitions: this.getColumnDefinitions({