From ed6824c5b92ffbd42babb02c68ca0808369d9669 Mon Sep 17 00:00:00 2001 From: Sjohn21 Date: Thu, 18 Jun 2026 18:30:43 +0200 Subject: [PATCH] Refine asset profiles response structure * Move the asset profile item type to the responses interfaces * Rename the response payload from marketData to assetProfiles * Align asset profile response mapping --- .../asset-profiles/asset-profiles.service.ts | 129 +++++++++--------- .../admin-market-data.component.ts | 10 +- libs/common/src/lib/interfaces/index.ts | 8 +- .../asset-profile-item.interface.ts} | 12 +- .../src/lib/assistant/assistant.component.ts | 4 +- 5 files changed, 82 insertions(+), 81 deletions(-) rename libs/common/src/lib/interfaces/{asset-profiles-response.interface.ts => responses/asset-profile-item.interface.ts} (94%) diff --git a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts index 4e35f1159..25d508b50 100644 --- a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts +++ b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts @@ -160,7 +160,7 @@ export class AssetProfilesService { }), this.prismaService.symbolProfile.count({ where }) ]); - const assetProfiles = symbolProfileResult[0]; + const symbolProfiles = symbolProfileResult[0]; let count = symbolProfileResult[1]; const lastMarketPrices = await this.prismaService.marketData.findMany({ @@ -173,12 +173,12 @@ export class AssetProfilesService { }, where: { dataSource: { - in: assetProfiles.map(({ dataSource }) => { + in: symbolProfiles.map(({ dataSource }) => { return dataSource; }) }, symbol: { - in: assetProfiles.map(({ symbol }) => { + in: symbolProfiles.map(({ symbol }) => { return symbol; }) } @@ -194,8 +194,8 @@ export class AssetProfilesService { ); } - let marketData: AssetProfileItem[] = await Promise.all( - assetProfiles.map(async (assetProfile) => { + let assetProfiles: AssetProfileItem[] = await Promise.all( + symbolProfiles.map(async (assetProfile) => { const { _count, activities, @@ -239,8 +239,8 @@ export class AssetProfilesService { currency, dataSource, id, - isin, isActive, + isin, lastMarketPrice, marketDataItemCount, name, @@ -256,21 +256,21 @@ export class AssetProfilesService { if (presetId) { if (presetId === 'ETF_WITHOUT_COUNTRIES') { - marketData = marketData.filter(({ countriesCount }) => { + assetProfiles = assetProfiles.filter(({ countriesCount }) => { return countriesCount === 0; }); } else if (presetId === 'ETF_WITHOUT_SECTORS') { - marketData = marketData.filter(({ sectorsCount }) => { + assetProfiles = assetProfiles.filter(({ sectorsCount }) => { return sectorsCount === 0; }); } - count = marketData.length; + count = assetProfiles.length; } return { - count, - marketData + assetProfiles, + count }; } @@ -348,50 +348,6 @@ export class AssetProfilesService { return data; } - private getExtendedPrismaClient() { - const symbolProfileExtension = Prisma.defineExtension((client) => { - return client.$extends({ - result: { - symbolProfile: { - isUsedByUsersWithSubscription: { - compute: async ({ id }) => { - const { _count } = - await this.prismaService.symbolProfile.findUnique({ - select: { - _count: { - select: { - activities: { - where: { - user: { - subscriptions: { - some: { - expiresAt: { - gt: new Date() - } - } - } - } - } - } - } - } - }, - where: { - id - } - }); - - return _count.activities > 0; - } - } - } - } - }); - }); - - return this.prismaService.$extends(symbolProfileExtension); - } - private async getAssetProfilesForCurrencies(): Promise { const currencyPairs = this.exchangeRateDataService.getCurrencyPairs(); @@ -432,7 +388,7 @@ export class AssetProfilesService { ); } - const marketDataPromise: Promise[] = currencyPairs.map( + const assetProfilePromises: Promise[] = currencyPairs.map( async ({ dataSource, symbol }) => { let activitiesCount: EnhancedSymbolProfile['activitiesCount'] = 0; let currency: EnhancedSymbolProfile['currency'] = '-'; @@ -458,26 +414,71 @@ export class AssetProfilesService { return { activitiesCount, - currency, - dataSource, - isin: null, - lastMarketPrice, - marketDataItemCount, - symbol, assetClass: AssetClass.LIQUIDITY, assetSubClass: AssetSubClass.CASH, + comment: null, countriesCount: 0, + currency, + dataSource, date: dateOfFirstActivity, id: undefined, isActive: true, + isin: null, + lastMarketPrice, + marketDataItemCount, name: symbol, sectorsCount: 0, + symbol, watchedByCount: 0 }; } ); - const marketData = await Promise.all(marketDataPromise); - return { marketData, count: marketData.length }; + const assetProfiles = await Promise.all(assetProfilePromises); + return { assetProfiles, count: assetProfiles.length }; + } + + private getExtendedPrismaClient() { + const symbolProfileExtension = Prisma.defineExtension((client) => { + return client.$extends({ + result: { + symbolProfile: { + isUsedByUsersWithSubscription: { + compute: async ({ id }) => { + const { _count } = + await this.prismaService.symbolProfile.findUnique({ + select: { + _count: { + select: { + activities: { + where: { + user: { + subscriptions: { + some: { + expiresAt: { + gt: new Date() + } + } + } + } + } + } + } + } + }, + where: { + id + } + }); + + return _count.activities > 0; + } + } + } + } + }); + }); + + return this.prismaService.$extends(symbolProfileExtension); } } diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index 89f2434cc..8874da97e 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -10,11 +10,11 @@ import { } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, + AssetProfileItem, Filter, InfoItem, User } from '@ghostfolio/common/interfaces'; -import { AssetProfileItem } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { GfSymbolPipe } from '@ghostfolio/common/pipes'; import { GfActivitiesFilterComponent } from '@ghostfolio/ui/activities-filter'; @@ -384,15 +384,15 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit { take: this.pageSize }) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(({ count, marketData }) => { + .subscribe(({ assetProfiles, count }) => { this.totalItems = count; this.dataSource = new MatTableDataSource( - marketData.map((marketDataItem) => { + assetProfiles.map((assetProfile) => { return { - ...marketDataItem, + ...assetProfile, isBenchmark: this.benchmarks.some(({ id }) => { - return id === marketDataItem.id; + return id === assetProfile.id; }) }; }) diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 08cb52e7d..bf70248f0 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -7,10 +7,6 @@ import type { AdminMarketDataDetails } from './admin-market-data-details.interfa import type { AdminUser } from './admin-user.interface'; import type { AssetClassSelectorOption } from './asset-class-selector-option.interface'; import type { AssetProfileIdentifier } from './asset-profile-identifier.interface'; -import type { - AssetProfileItem, - AssetProfilesResponse -} from './asset-profiles-response.interface'; import type { BenchmarkProperty } from './benchmark-property.interface'; import type { Benchmark } from './benchmark.interface'; import type { Coupon } from './coupon.interface'; @@ -46,6 +42,10 @@ import type { AdminUsersResponse } from './responses/admin-users-response.interf import type { AiPromptResponse } from './responses/ai-prompt-response.interface'; import type { AiServiceHealthResponse } from './responses/ai-service-health-response.interface'; import type { ApiKeyResponse } from './responses/api-key-response.interface'; +import type { + AssetProfileItem, + AssetProfilesResponse +} from './responses/asset-profile-item.interface'; import type { AssetResponse } from './responses/asset-response.interface'; import type { BenchmarkMarketDataDetailsResponse } from './responses/benchmark-market-data-details-response.interface'; import type { BenchmarkResponse } from './responses/benchmark-response.interface'; diff --git a/libs/common/src/lib/interfaces/asset-profiles-response.interface.ts b/libs/common/src/lib/interfaces/responses/asset-profile-item.interface.ts similarity index 94% rename from libs/common/src/lib/interfaces/asset-profiles-response.interface.ts rename to libs/common/src/lib/interfaces/responses/asset-profile-item.interface.ts index 7fa61af3d..82105789c 100644 --- a/libs/common/src/lib/interfaces/asset-profiles-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/asset-profile-item.interface.ts @@ -1,10 +1,5 @@ import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; -export interface AssetProfilesResponse { - count: number; - marketData: AssetProfileItem[]; -} - export interface AssetProfileItem { activitiesCount: number; assetClass?: AssetClass; @@ -15,9 +10,9 @@ export interface AssetProfileItem { dataSource: DataSource; date: Date; id: string; - isin?: string | null; isActive: boolean; isBenchmark?: boolean; + isin?: string | null; isUsedByUsersWithSubscription?: boolean; lastMarketPrice: number; marketDataItemCount: number; @@ -26,3 +21,8 @@ export interface AssetProfileItem { symbol: string; watchedByCount: number; } + +export interface AssetProfilesResponse { + assetProfiles: AssetProfileItem[]; + count: number; +} diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 7c4ce9355..e23190939 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -688,8 +688,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { catchError(() => { return EMPTY; }), - map(({ marketData }) => { - return marketData.map( + map(({ assetProfiles }) => { + return assetProfiles.map( ({ assetSubClass, currency, dataSource, name, symbol }) => { return { currency,