From aee0b647ef35fd5b6aba3f7ecccfa5f6b13615b7 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:45:17 +0200 Subject: [PATCH] Improve validation of activities and asset profiles when combining custom asset profile symbol with data source other than MANUAL --- apps/api/src/app/import/import.service.ts | 39 +++++++++++++------ .../data-provider/data-provider.service.ts | 2 +- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 909983e23..3d37aaeb9 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -3,7 +3,9 @@ import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.ser import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { getTagsWithDraftTag } from '@ghostfolio/api/helper/activity.helper'; +import { getMaskedGhostfolioDataSource } from '@ghostfolio/api/helper/data-source.helper'; import { ApiService } from '@ghostfolio/api/services/api/api.service'; +import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; @@ -56,6 +58,7 @@ export class ImportService { private readonly accountService: AccountService, private readonly activitiesService: ActivitiesService, private readonly apiService: ApiService, + private readonly configurationService: ConfigurationService, private readonly dataGatheringService: DataGatheringService, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, @@ -204,6 +207,9 @@ export class ImportService { }): Promise { const accountIdMapping: { [oldAccountId: string]: string } = {}; const assetProfileSymbolMapping: { [oldSymbol: string]: string } = {}; + const ghostfolioDataSources = this.configurationService.get( + 'DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER' + ); const platformIdMapping: { [oldPlatformId: string]: string } = {}; const tagIdMapping: { [oldTagId: string]: string } = {}; const userCurrency = user.settings.settings.baseCurrency; @@ -224,18 +230,22 @@ export class ImportService { } } - // Validate the symbols before any data is persisted. Activities without a - // data source are excluded, since a symbol is generated in - // createActivity() if needed. + // Validate the symbols before any data is persisted. Activities without an + // explicit data source are excluded from the first check, since a symbol is + // generated in createActivity() if needed. for (const [index, activity] of activitiesDto.entries()) { - if (!activity.dataSource) { - if (NON_INVESTMENT_ACTIVITY_TYPES.includes(activity.type)) { - activity.dataSource = DataSource.MANUAL; - } else { - activity.dataSource = - this.dataProviderService.getDataSourceForImport(); - } - } else if ( + const hasDataSource = Boolean(activity.dataSource); + + if (!hasDataSource) { + activity.dataSource = NON_INVESTMENT_ACTIVITY_TYPES.includes( + activity.type + ) + ? DataSource.MANUAL + : this.dataProviderService.getDataSourceForImport(); + } + + if ( + hasDataSource && activity.dataSource === DataSource.MANUAL && !isValidCustomAssetProfileSymbol(activity.symbol) ) { @@ -246,8 +256,13 @@ export class ImportService { activity.dataSource !== DataSource.MANUAL && isValidCustomAssetProfileSymbol(activity.symbol) ) { + const maskedDataSource = getMaskedGhostfolioDataSource({ + ghostfolioDataSources, + dataSource: activity.dataSource + }); + throw new Error( - `activities.${index}.symbol ("${activity.symbol}") is not valid for the data source ("${activity.dataSource}")` + `activities.${index}.symbol ("${activity.symbol}") is not valid for the data source ("${maskedDataSource}")` ); } } diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index d8a3d79ef..5dd274751 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -329,7 +329,7 @@ export class DataProviderService implements OnModuleInit { if (!assetProfile?.name) { throw new Error( - `${activityPath}.symbol ("${symbol}") is not valid for the specified data source ("${maskedDataSource}")` + `${activityPath}.symbol ("${symbol}") cannot be resolved by the data source ("${maskedDataSource}")` ); }