Browse Source

Improve validation of activities and asset profiles when combining custom asset profile symbol with data source other than MANUAL

pull/7721/head
Thomas Kaul 4 days ago
parent
commit
aee0b647ef
  1. 39
      apps/api/src/app/import/import.service.ts
  2. 2
      apps/api/src/services/data-provider/data-provider.service.ts

39
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 { PlatformService } from '@ghostfolio/api/app/platform/platform.service';
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
import { getTagsWithDraftTag } from '@ghostfolio/api/helper/activity.helper'; 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 { 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 { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.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'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
@ -56,6 +58,7 @@ export class ImportService {
private readonly accountService: AccountService, private readonly accountService: AccountService,
private readonly activitiesService: ActivitiesService, private readonly activitiesService: ActivitiesService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly configurationService: ConfigurationService,
private readonly dataGatheringService: DataGatheringService, private readonly dataGatheringService: DataGatheringService,
private readonly dataProviderService: DataProviderService, private readonly dataProviderService: DataProviderService,
private readonly exchangeRateDataService: ExchangeRateDataService, private readonly exchangeRateDataService: ExchangeRateDataService,
@ -204,6 +207,9 @@ export class ImportService {
}): Promise<Activity[]> { }): Promise<Activity[]> {
const accountIdMapping: { [oldAccountId: string]: string } = {}; const accountIdMapping: { [oldAccountId: string]: string } = {};
const assetProfileSymbolMapping: { [oldSymbol: string]: string } = {}; const assetProfileSymbolMapping: { [oldSymbol: string]: string } = {};
const ghostfolioDataSources = this.configurationService.get(
'DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER'
);
const platformIdMapping: { [oldPlatformId: string]: string } = {}; const platformIdMapping: { [oldPlatformId: string]: string } = {};
const tagIdMapping: { [oldTagId: string]: string } = {}; const tagIdMapping: { [oldTagId: string]: string } = {};
const userCurrency = user.settings.settings.baseCurrency; const userCurrency = user.settings.settings.baseCurrency;
@ -224,18 +230,22 @@ export class ImportService {
} }
} }
// Validate the symbols before any data is persisted. Activities without a // Validate the symbols before any data is persisted. Activities without an
// data source are excluded, since a symbol is generated in // explicit data source are excluded from the first check, since a symbol is
// createActivity() if needed. // generated in createActivity() if needed.
for (const [index, activity] of activitiesDto.entries()) { for (const [index, activity] of activitiesDto.entries()) {
if (!activity.dataSource) { const hasDataSource = Boolean(activity.dataSource);
if (NON_INVESTMENT_ACTIVITY_TYPES.includes(activity.type)) {
activity.dataSource = DataSource.MANUAL; if (!hasDataSource) {
} else { activity.dataSource = NON_INVESTMENT_ACTIVITY_TYPES.includes(
activity.dataSource = activity.type
this.dataProviderService.getDataSourceForImport(); )
} ? DataSource.MANUAL
} else if ( : this.dataProviderService.getDataSourceForImport();
}
if (
hasDataSource &&
activity.dataSource === DataSource.MANUAL && activity.dataSource === DataSource.MANUAL &&
!isValidCustomAssetProfileSymbol(activity.symbol) !isValidCustomAssetProfileSymbol(activity.symbol)
) { ) {
@ -246,8 +256,13 @@ export class ImportService {
activity.dataSource !== DataSource.MANUAL && activity.dataSource !== DataSource.MANUAL &&
isValidCustomAssetProfileSymbol(activity.symbol) isValidCustomAssetProfileSymbol(activity.symbol)
) { ) {
const maskedDataSource = getMaskedGhostfolioDataSource({
ghostfolioDataSources,
dataSource: activity.dataSource
});
throw new Error( 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}")`
); );
} }
} }

2
apps/api/src/services/data-provider/data-provider.service.ts

@ -329,7 +329,7 @@ export class DataProviderService implements OnModuleInit {
if (!assetProfile?.name) { if (!assetProfile?.name) {
throw new Error( 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}")`
); );
} }

Loading…
Cancel
Save