From e8f7c34a461c53011f866165d0306d9597dcb44a Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:28:35 +0200 Subject: [PATCH] Task/deprecate symbol profile in create activity endpoint (#8026) * Deprecate SymbolProfile in favor of assetProfile * Update changelog --- CHANGELOG.md | 1 + .../app/activities/activities.controller.ts | 21 ++++++++++++---- apps/api/src/helper/object.helper.spec.ts | 25 +++++++++++++++++-- ...orm-data-source-in-response.interceptor.ts | 4 +++ libs/common/src/lib/config.ts | 7 ++++++ libs/common/src/lib/interfaces/index.ts | 2 ++ .../create-activity-response.interface.ts | 12 +++++++++ libs/ui/src/lib/services/data.service.ts | 3 ++- 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 libs/common/src/lib/interfaces/responses/create-activity-response.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c8cac1379..366727022c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Deprecated `SymbolProfile` in favor of `assetProfile` in the endpoint `POST api/v1/activities` - Upgraded `Nx` from version `23.1.1` to `23.2.1` ### Fixed diff --git a/apps/api/src/app/activities/activities.controller.ts b/apps/api/src/app/activities/activities.controller.ts index 90851b2ca4..28ce890449 100644 --- a/apps/api/src/app/activities/activities.controller.ts +++ b/apps/api/src/app/activities/activities.controller.ts @@ -9,13 +9,15 @@ import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interc import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; +import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { DATA_GATHERING_QUEUE_PRIORITY_HIGH } from '@ghostfolio/common/config'; import { CreateOrderDto, UpdateOrderDto } from '@ghostfolio/common/dtos'; import { SubscriptionType } from '@ghostfolio/common/enums'; import { ActivitiesResponse, - ActivityResponse + ActivityResponse, + CreateActivityResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import { scopes } from '@ghostfolio/common/scopes'; @@ -33,7 +35,7 @@ import { Query, UseInterceptors } from '@nestjs/common'; -import { Order, Prisma } from '@prisma/client'; +import { Order } from '@prisma/client'; import { parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; @@ -47,7 +49,8 @@ export class ActivitiesController { private readonly activitiesService: ActivitiesService, private readonly apiService: ApiService, private readonly dataProviderService: DataProviderService, - private readonly dataGatheringService: DataGatheringService + private readonly dataGatheringService: DataGatheringService, + private readonly symbolProfileService: SymbolProfileService ) {} @Delete() @@ -209,6 +212,7 @@ export class ActivitiesController { @RequiresScope(scopes.activityCreate) @UseInterceptors(RedactValuesInResponseInterceptor) @UseInterceptors(TransformDataSourceInRequestInterceptor) + @UseInterceptors(TransformDataSourceInResponseInterceptor) public async createActivity( @Body() data: CreateOrderDto, @Impersonation() @@ -217,7 +221,7 @@ export class ActivitiesController { userId, userSubscription }: ImpersonationContext - ): Promise> { + ): Promise { // Evaluate the more restrictive subscription of the authenticated user // and the owner of the activity const subscription = @@ -303,7 +307,14 @@ export class ActivitiesController { }); } - return activity; + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ + { + dataSource: activity.SymbolProfile.dataSource, + symbol: activity.SymbolProfile.symbol + } + ]); + + return { ...activity, assetProfile }; } @HasPermission(permissions.updateActivity) diff --git a/apps/api/src/helper/object.helper.spec.ts b/apps/api/src/helper/object.helper.spec.ts index 5543441dbc..773b7905ae 100644 --- a/apps/api/src/helper/object.helper.spec.ts +++ b/apps/api/src/helper/object.helper.spec.ts @@ -3121,11 +3121,24 @@ describe('redactAttributes', () => { }); // The endpoint POST api/v1/activities returns a row of the database with the - // relation to the symbol profile - it('should redact an activity with the relation to the symbol profile', () => { + // asset profile and the deprecated relation to the symbol profile + it('should redact an activity with the asset profile and the relation to the symbol profile', () => { expect( redactPaths({ object: { + assetProfile: { + comment: 'Reviewed by the administrator', + name: 'Apple Inc', + scraperConfiguration: { + headers: { Authorization: 'Bearer TOKEN' }, + mode: 'lazy', + selector: '.price', + url: 'https://example.org/get_price' + }, + symbol: 'AAPL', + symbolMapping: { YAHOO: 'AAPL' }, + watchedByCount: 7 + }, comment: 'Bought on a dip', currency: 'USD', fee: 19.9, @@ -3149,6 +3162,14 @@ describe('redactAttributes', () => { paths: DEFAULT_REDACTED_PATHS }) ).toStrictEqual({ + assetProfile: { + comment: null, + name: 'Apple Inc', + scraperConfiguration: null, + symbol: 'AAPL', + symbolMapping: null, + watchedByCount: null + }, comment: null, currency: 'USD', fee: null, diff --git a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts index eedc808772..296925af92 100644 --- a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts @@ -94,6 +94,10 @@ export class TransformDataSourceInResponseInterceptor< 'items[*].dataSource', 'latestActivities[*].assetProfile.dataSource', 'settings["filters.dataSource"]', + + /* @deprecated */ + 'SymbolProfile.dataSource', + 'watchlist[*].dataSource' ] }); diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index dbf873df2a..73bf5cc00c 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -180,9 +180,16 @@ export const DEFAULT_REDACTED_PATHS = [ 'settings.emergencyFund', 'settings.projectedTotalAmount', 'settings.savingsRate', + + /* @deprecated */ 'SymbolProfile.comment', + + /* @deprecated */ 'SymbolProfile.scraperConfiguration', + + /* @deprecated */ 'SymbolProfile.symbolMapping', + 'totalBalanceInBaseCurrency', 'totalDividendInBaseCurrency', 'totalInterestInBaseCurrency', diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 113e485b74..7e663f5776 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -53,6 +53,7 @@ import type { AssetProfilesResponse } from './responses/asset-profiles-response. 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'; +import type { CreateActivityResponse } from './responses/create-activity-response.interface'; import type { CreateStripeCheckoutSessionResponse } from './responses/create-stripe-checkout-session-response.interface'; import type { DataEnhancerHealthResponse } from './responses/data-enhancer-health-response.interface'; import type { DataProviderGhostfolioAssetProfileResponse } from './responses/data-provider-ghostfolio-asset-profile-response.interface'; @@ -133,6 +134,7 @@ export { BenchmarkProperty, BenchmarkResponse, Coupon, + CreateActivityResponse, CreateStripeCheckoutSessionResponse, DataEnhancerHealthResponse, DataProviderGhostfolioAssetProfileResponse, diff --git a/libs/common/src/lib/interfaces/responses/create-activity-response.interface.ts b/libs/common/src/lib/interfaces/responses/create-activity-response.interface.ts new file mode 100644 index 0000000000..198a2cef79 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/create-activity-response.interface.ts @@ -0,0 +1,12 @@ +import type { Order, SymbolProfile } from '@ghostfolio/prisma/browser'; + +import { EnhancedAssetProfile } from '../enhanced-asset-profile.interface'; + +export interface CreateActivityResponse extends Order { + assetProfile: EnhancedAssetProfile; + + /** + * @deprecated Use `assetProfile` instead + */ + SymbolProfile: SymbolProfile; +} diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 8545c36ba4..f18b3eef13 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -34,6 +34,7 @@ import { AssetResponse, BenchmarkMarketDataDetailsResponse, BenchmarkResponse, + CreateActivityResponse, CreateStripeCheckoutSessionResponse, DataProviderHealthResponse, DataProviderHistoricalResponse, @@ -873,7 +874,7 @@ export class DataService { } public postActivity(aOrder: CreateOrderDto) { - return this.http.post('/api/v1/activities', aOrder); + return this.http.post('/api/v1/activities', aOrder); } public postApiKey() {