From 45c5c78b64e4bef79ea2cb131866006cf9c25bb4 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 26 Jul 2025 17:17:51 +0200 Subject: [PATCH] Restrict data source in CreateAssetProfileWithMarketDataDto --- ...ate-asset-profile-with-market-data.dto.ts} | 8 +- apps/api/src/app/import/import-data.dto.ts | 2 +- apps/api/src/app/import/import.service.ts | 100 ++++++++---------- .../import-activities-dialog.component.ts | 2 +- .../app/services/import-activities.service.ts | 2 +- 5 files changed, 54 insertions(+), 60 deletions(-) rename apps/api/src/app/import/{create-asset-profile-with-maketdata.dto.ts => create-asset-profile-with-market-data.dto.ts} (54%) diff --git a/apps/api/src/app/import/create-asset-profile-with-maketdata.dto.ts b/apps/api/src/app/import/create-asset-profile-with-market-data.dto.ts similarity index 54% rename from apps/api/src/app/import/create-asset-profile-with-maketdata.dto.ts rename to apps/api/src/app/import/create-asset-profile-with-market-data.dto.ts index 92fc1e1ca..fd90ab1af 100644 --- a/apps/api/src/app/import/create-asset-profile-with-maketdata.dto.ts +++ b/apps/api/src/app/import/create-asset-profile-with-market-data.dto.ts @@ -1,10 +1,16 @@ import { MarketData } from '@ghostfolio/common/interfaces'; -import { IsArray, IsOptional } from 'class-validator'; +import { DataSource } from '@prisma/client'; +import { IsArray, IsEnum, IsOptional } from 'class-validator'; import { CreateAssetProfileDto } from '../admin/create-asset-profile.dto'; export class CreateAssetProfileWithMarketDataDto extends CreateAssetProfileDto { + @IsEnum([DataSource.MANUAL], { + message: `dataSource must be '${DataSource.MANUAL}'` + }) + dataSource: DataSource; + @IsArray() @IsOptional() marketData?: MarketData[]; diff --git a/apps/api/src/app/import/import-data.dto.ts b/apps/api/src/app/import/import-data.dto.ts index 6b28891ed..138d16961 100644 --- a/apps/api/src/app/import/import-data.dto.ts +++ b/apps/api/src/app/import/import-data.dto.ts @@ -4,7 +4,7 @@ import { Type } from 'class-transformer'; import { IsArray, IsOptional, ValidateNested } from 'class-validator'; import { CreateAccountWithBalancesDto } from './create-account-with-balances.dto'; -import { CreateAssetProfileWithMarketDataDto } from './create-asset-profile-with-maketdata.dto'; +import { CreateAssetProfileWithMarketDataDto } from './create-asset-profile-with-market-data.dto'; export class ImportDataDto { @IsArray() diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index d229098e2..d23427616 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -237,71 +237,59 @@ export class ImportService { } if (!isDryRun && assetProfilesWithMarketDataDto?.length) { - // Filter out not custom asset profiles - assetProfilesWithMarketDataDto = assetProfilesWithMarketDataDto.filter( - ({ dataSource }) => { - return dataSource === DataSource.MANUAL; - } - ); + const existingAssetProfiles = + await this.symbolProfileService.getSymbolProfiles( + assetProfilesWithMarketDataDto.map(({ dataSource, symbol }) => { + return { dataSource, symbol }; + }) + ); - if (assetProfilesWithMarketDataDto.length) { - const existingAssetProfiles = - await this.symbolProfileService.getSymbolProfiles( - assetProfilesWithMarketDataDto.map(({ dataSource, symbol }) => { - return { dataSource, symbol }; - }) - ); + for (const assetProfileWithMarketData of assetProfilesWithMarketDataDto) { + // Check if there is any existing asset profile + const existingAssetProfile = existingAssetProfiles.find( + ({ dataSource, symbol }) => { + return ( + dataSource === assetProfileWithMarketData.dataSource && + symbol === assetProfileWithMarketData.symbol + ); + } + ); - for (const assetProfileWithMarketData of assetProfilesWithMarketDataDto) { - // Check if there is any existing asset profile - const existingAssetProfile = existingAssetProfiles.find( - ({ dataSource, symbol }) => { - return ( - dataSource === assetProfileWithMarketData.dataSource && - symbol === assetProfileWithMarketData.symbol - ); - } + // If there is no asset profile or if the asset profile belongs to a different user, then create a new asset profile + if (!existingAssetProfile || existingAssetProfile.userId !== user.id) { + const assetProfile: CreateAssetProfileDto = omit( + assetProfileWithMarketData, + 'marketData' ); - // If there is no asset profile or if the asset profile belongs to a different user, then create a new asset profile - if ( - !existingAssetProfile || - existingAssetProfile.userId !== user.id - ) { - const assetProfile: CreateAssetProfileDto = omit( - assetProfileWithMarketData, - 'marketData' - ); + // Asset profile belongs to a different user + if (existingAssetProfile) { + const symbol = uuidv4(); + assetProfileSymbolMapping[assetProfile.symbol] = symbol; + assetProfile.symbol = symbol; + } - // Asset profile belongs to a different user - if (existingAssetProfile) { - const symbol = uuidv4(); // Generate a new symbol for the asset profile - assetProfileSymbolMapping[assetProfile.symbol] = symbol; - assetProfile.symbol = symbol; - } + // Create a new asset profile + const assetProfileObject: Prisma.SymbolProfileCreateInput = { + ...assetProfile, + user: { connect: { id: user.id } } + }; - // Create a new asset profile - const assetProfileObject: Prisma.SymbolProfileCreateInput = { - ...assetProfile, - user: { connect: { id: user.id } } - }; + await this.symbolProfileService.add(assetProfileObject); + } - await this.symbolProfileService.add(assetProfileObject); + // Insert or update market data + const marketDataObjects = assetProfileWithMarketData.marketData.map( + (marketData) => { + return { + ...marketData, + dataSource: assetProfileWithMarketData.dataSource, + symbol: assetProfileWithMarketData.symbol + } as Prisma.MarketDataUpdateInput; } + ); - // Insert or update market data - const marketDataObjects = assetProfileWithMarketData.marketData.map( - (marketData) => { - return { - ...marketData, - dataSource: assetProfileWithMarketData.dataSource, - symbol: assetProfileWithMarketData.symbol - } as Prisma.MarketDataUpdateInput; - } - ); - - await this.marketDataService.updateMany({ data: marketDataObjects }); - } + await this.marketDataService.updateMany({ data: marketDataObjects }); } } diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index 99adfa2cc..660e7265e 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -1,5 +1,5 @@ import { CreateAccountWithBalancesDto } from '@ghostfolio/api/app/import/create-account-with-balances.dto'; -import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-maketdata.dto'; +import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-market-data.dto'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module'; import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 6c331cc2c..033ae7e24 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -1,5 +1,5 @@ import { CreateAccountWithBalancesDto } from '@ghostfolio/api/app/import/create-account-with-balances.dto'; -import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-maketdata.dto'; +import { CreateAssetProfileWithMarketDataDto } from '@ghostfolio/api/app/import/create-asset-profile-with-market-data.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { parseDate as parseDateHelper } from '@ghostfolio/common/helper';