diff --git a/CHANGELOG.md b/CHANGELOG.md index 297677b966..89303e3443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed the missing localization of the date picker in the asset profile details dialog of the admin control panel - Fixed the missing tooltip of the client of the last request in the users table of the admin control panel +### Fixed + +- Fixed an issue with the query parameter limit of the database in the activities of users with many activities + ## 3.88.0 - 2026-10-17 ### Added diff --git a/apps/api/src/app/activities/activities.service.spec.ts b/apps/api/src/app/activities/activities.service.spec.ts index b32d209ad0..df86b04da3 100644 --- a/apps/api/src/app/activities/activities.service.spec.ts +++ b/apps/api/src/app/activities/activities.service.spec.ts @@ -267,6 +267,7 @@ describe('ActivitiesService', () => { } as unknown as ExchangeRateDataService, null, { + account: { findMany: jest.fn().mockResolvedValue([]) }, order: { findMany, count: jest.fn().mockResolvedValue(1) @@ -302,6 +303,87 @@ describe('ActivitiesService', () => { expect(activities[0].assetProfile).toBe(assetProfile); }); + it('returns the activities with their account without including it in the query of the activities', async () => { + const findMany = jest.fn().mockResolvedValue([ + { + ...activityDummyData, + accountId: 'account-id', + accountUserId: 'user-id', + currency: 'USD', + quantity: 1, + unitPrice: 100, + SymbolProfile: { + currency: 'USD', + dataSource: DataSource.YAHOO, + symbol: 'AAPL' + }, + tags: [] + }, + { + ...activityDummyData, + accountId: null, + accountUserId: null, + currency: 'USD', + quantity: 1, + unitPrice: 100, + SymbolProfile: { + currency: 'USD', + dataSource: DataSource.YAHOO, + symbol: 'AAPL' + }, + tags: [] + } + ]); + + const service = new ActivitiesService( + null, + null, + null, + null, + null, + null, + null, + { + toCurrencyAtDate: jest.fn().mockResolvedValue(0) + } as unknown as ExchangeRateDataService, + null, + { + account: { + findMany: jest.fn().mockResolvedValue([ + { + id: 'account-id', + name: 'Brokerage', + platform: { id: 'platform-id', name: 'Broker' }, + tags: [{ tag: { id: 'tag-id', name: 'Tag' } }], + userId: 'user-id' + } + ]) + }, + order: { + findMany, + count: jest.fn().mockResolvedValue(2) + } + } as unknown as PrismaService, + { + getSymbolProfiles: jest.fn().mockResolvedValue([]) + } as unknown as SymbolProfileService, + null + ); + + const { activities } = await service.getActivities({ + userCurrency: 'USD', + userId: 'user-id' + }); + + expect(findMany.mock.calls[0][0].include).not.toHaveProperty('account'); + expect(activities[0].account).toMatchObject({ + id: 'account-id', + platform: { id: 'platform-id', name: 'Broker' }, + tags: [{ id: 'tag-id', name: 'Tag' }] + }); + expect(activities[1].account).toBeNull(); + }); + it('excludes the activities of the start date of a date range', async () => { jest.useFakeTimers().setSystemTime(parseDate('2024-06-15').getTime()); @@ -333,6 +415,7 @@ describe('ActivitiesService', () => { null, null, { + account: { findMany: jest.fn().mockResolvedValue([]) }, order: { findMany, count: jest.fn().mockResolvedValue(0) diff --git a/apps/api/src/app/activities/activities.service.ts b/apps/api/src/app/activities/activities.service.ts index 4a20658f97..7c9e558702 100644 --- a/apps/api/src/app/activities/activities.service.ts +++ b/apps/api/src/app/activities/activities.service.ts @@ -652,22 +652,12 @@ export class ActivitiesService { withExcludedAccountsAndActivities }); - const [orders, count] = await Promise.all([ + const [orders, count, accounts] = await Promise.all([ this.orders({ skip, take, where, include: { - account: { - include: { - platform: true, - tags: { - include: { - tag: true - } - } - } - }, // eslint-disable-next-line @typescript-eslint/naming-convention SymbolProfile: { select: { @@ -680,17 +670,35 @@ export class ActivitiesService { }, orderBy: [...orderBy, { id: sortDirection }] }), - this.prismaService.order.count({ where }) + this.prismaService.order.count({ where }), + // Loaded separately, since including the account (composite key) in the + // query above exceeds the query parameter limit for many activities + this.prismaService.account.findMany({ + include: { + platform: true, + tags: { + include: { + tag: true + } + } + }, + where: { userId } + }) ]); for (const order of orders) { - if (order.account) { - order.account.tags = ( - order.account.tags as unknown as { tag: Tag }[] - ).map(({ tag }) => { - return tag; - }); - } + const account = accounts.find(({ id, userId: accountUserId }) => { + return id === order.accountId && accountUserId === order.accountUserId; + }); + + order.account = account + ? { + ...account, + tags: account.tags.map(({ tag }) => { + return tag; + }) + } + : null; } const assetProfileIdentifiers = uniqBy(