diff --git a/CHANGELOG.md b/CHANGELOG.md index ef6904603..db47c8b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Extended the filters of the activities endpoint by `dataSource` and `symbol` + ### Changed - Optimized the asynchronous operations using `Promise.all()` in the info service diff --git a/apps/api/src/app/order/order.controller.ts b/apps/api/src/app/order/order.controller.ts index 7a9cf3d17..f2acac5e4 100644 --- a/apps/api/src/app/order/order.controller.ts +++ b/apps/api/src/app/order/order.controller.ts @@ -94,15 +94,18 @@ export class OrderController { @Get() @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(RedactValuesInResponseInterceptor) + @UseInterceptors(TransformDataSourceInRequestInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getAllOrders( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId, @Query('accounts') filterByAccounts?: string, @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, @Query('range') dateRange?: DateRange, @Query('skip') skip?: number, @Query('sortColumn') sortColumn?: string, @Query('sortDirection') sortDirection?: Prisma.SortOrder, + @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string, @Query('take') take?: number ): Promise { @@ -116,6 +119,8 @@ export class OrderController { const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, filterByAssetClasses, + filterByDataSource, + filterBySymbol, filterByTags }); diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index d9ff68d61..dcbc20e2b 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -350,6 +350,14 @@ export class OrderService { return type; }); + const filterByDataSource = filters?.find(({ type }) => { + return type === 'DATA_SOURCE'; + })?.id; + + const filterBySymbol = filters?.find(({ type }) => { + return type === 'SYMBOL'; + })?.id; + const searchQuery = filters?.find(({ type }) => { return type === 'SEARCH_QUERY'; })?.id; @@ -395,6 +403,29 @@ export class OrderService { }; } + if (filterByDataSource && filterBySymbol) { + if (where.SymbolProfile) { + where.SymbolProfile = { + AND: [ + where.SymbolProfile, + { + AND: [ + { dataSource: filterByDataSource }, + { symbol: filterBySymbol } + ] + } + ] + }; + } else { + where.SymbolProfile = { + AND: [ + { dataSource: filterByDataSource }, + { symbol: filterBySymbol } + ] + }; + } + } + if (searchQuery) { const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [ { id: { mode: 'insensitive', startsWith: searchQuery } }, diff --git a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts index df99c2da3..227d06d7b 100644 --- a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts @@ -39,12 +39,12 @@ export class TransformDataSourceInRequestInterceptor }); } - if (request.body.dataSource && !DataSource[request.body.dataSource]) { - request.body.dataSource = decodeDataSource(request.body.dataSource); - } + for (const type of ['body', 'params', 'query']) { + const dataSourceValue = request[type]?.dataSource; - if (request.params.dataSource && !DataSource[request.params.dataSource]) { - request.params.dataSource = decodeDataSource(request.params.dataSource); + if (dataSourceValue && !DataSource[dataSourceValue]) { + request[type].dataSource = decodeDataSource(dataSourceValue); + } } } diff --git a/apps/api/src/services/api/api.service.ts b/apps/api/src/services/api/api.service.ts index e961ec037..5bcc6bb1d 100644 --- a/apps/api/src/services/api/api.service.ts +++ b/apps/api/src/services/api/api.service.ts @@ -10,22 +10,28 @@ export class ApiService { filterByAccounts, filterByAssetClasses, filterByAssetSubClasses, + filterByDataSource, filterByHoldingType, filterBySearchQuery, + filterBySymbol, filterByTags }: { filterByAccounts?: string; filterByAssetClasses?: string; filterByAssetSubClasses?: string; + filterByDataSource?: string; filterByHoldingType?: string; filterBySearchQuery?: string; + filterBySymbol?: string; filterByTags?: string; }): Filter[] { const accountIds = filterByAccounts?.split(',') ?? []; const assetClasses = filterByAssetClasses?.split(',') ?? []; const assetSubClasses = filterByAssetSubClasses?.split(',') ?? []; + const dataSource = filterByDataSource; const holdingType = filterByHoldingType; const searchQuery = filterBySearchQuery?.toLowerCase(); + const symbol = filterBySymbol; const tagIds = filterByTags?.split(',') ?? []; const filters = [ @@ -55,6 +61,13 @@ export class ApiService { }) ]; + if (dataSource) { + filters.push({ + id: dataSource, + type: 'DATA_SOURCE' + }); + } + if (holdingType) { filters.push({ id: holdingType, @@ -69,6 +82,13 @@ export class ApiService { }); } + if (symbol) { + filters.push({ + id: symbol, + type: 'SYMBOL' + }); + } + return filters; } } diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index 64c062c7e..70cd08874 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -89,7 +89,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { public activityForm: FormGroup; public accounts: Account[]; - public activities: Activity[]; public assetClass: string; public assetSubClass: string; public averagePrice: number; @@ -174,6 +173,22 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { .subscribe(); }); + this.dataService + .fetchActivities({ + filters: [ + { id: this.data.dataSource, type: 'DATA_SOURCE' }, + { id: this.data.symbol, type: 'SYMBOL' } + ], + sortColumn: this.sortColumn, + sortDirection: this.sortDirection + }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ activities }) => { + this.dataSource = new MatTableDataSource(activities); + + this.changeDetectorRef.markForCheck(); + }); + this.dataService .fetchHoldingDetail({ dataSource: this.data.dataSource, @@ -198,7 +213,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { netPerformancePercent, netPerformancePercentWithCurrencyEffect, netPerformanceWithCurrencyEffect, - orders, quantity, SymbolProfile, tags, @@ -206,12 +220,10 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { value }) => { this.accounts = accounts; - this.activities = orders; this.averagePrice = averagePrice; this.benchmarkDataItems = []; this.countries = {}; this.dataProviderInfo = dataProviderInfo; - this.dataSource = new MatTableDataSource(orders.reverse()); this.dividendInBaseCurrency = dividendInBaseCurrency; if ( diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index fab5bc452..f92ad54f8 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -311,7 +311,7 @@ animationDuration="0" class="mb-5" [mat-stretch-tabs]="false" - [ngClass]="{ 'd-none': !activities?.length }" + [ngClass]="{ 'd-none': !dataSource?.data.length }" > @@ -422,7 +422,8 @@ } @if ( - activities?.length > 0 && data.hasPermissionToReportDataGlitch === true + dataSource?.data.length > 0 && + data.hasPermissionToReportDataGlitch === true ) {
diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 6de3d327d..64376a606 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -72,14 +72,24 @@ export class DataService { ACCOUNT: filtersByAccount, ASSET_CLASS: filtersByAssetClass, ASSET_SUB_CLASS: filtersByAssetSubClass, + DATA_SOURCE: [filterByDataSource], HOLDING_TYPE: filtersByHoldingType, PRESET_ID: filtersByPresetId, SEARCH_QUERY: filtersBySearchQuery, + SYMBOL: [filterBySymbol], TAG: filtersByTag } = groupBy(filters, (filter) => { return filter.type; }); + if (filterByDataSource) { + params = params.append('dataSource', filterByDataSource.id); + } + + if (filterBySymbol) { + params = params.append('symbol', filterBySymbol.id); + } + if (filtersByAccount) { params = params.append( 'accounts', diff --git a/libs/common/src/lib/interfaces/filter.interface.ts b/libs/common/src/lib/interfaces/filter.interface.ts index 3d555f796..43634f876 100644 --- a/libs/common/src/lib/interfaces/filter.interface.ts +++ b/libs/common/src/lib/interfaces/filter.interface.ts @@ -5,6 +5,7 @@ export interface Filter { | 'ACCOUNT' | 'ASSET_CLASS' | 'ASSET_SUB_CLASS' + | 'DATA_SOURCE' | 'HOLDING_TYPE' | 'PRESET_ID' | 'SEARCH_QUERY'