Browse Source

Feature/extend filters of order endpoint (#3743)

* Extend filters of order endpoint

* Update changelog

---------

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
pull/3748/head
Shaunak Das 5 months ago
committed by GitHub
parent
commit
383a02519a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 5
      apps/api/src/app/order/order.controller.ts
  3. 31
      apps/api/src/app/order/order.service.ts
  4. 10
      apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts
  5. 20
      apps/api/src/services/api/api.service.ts
  6. 20
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  7. 5
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
  8. 10
      apps/client/src/app/services/data.service.ts
  9. 1
      libs/common/src/lib/interfaces/filter.interface.ts

4
CHANGELOG.md

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Added
- Extended the filters of the activities endpoint by `dataSource` and `symbol`
### Changed ### Changed
- Optimized the asynchronous operations using `Promise.all()` in the info service - Optimized the asynchronous operations using `Promise.all()` in the info service

5
apps/api/src/app/order/order.controller.ts

@ -94,15 +94,18 @@ export class OrderController {
@Get() @Get()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor) @UseInterceptors(RedactValuesInResponseInterceptor)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
@UseInterceptors(TransformDataSourceInResponseInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getAllOrders( public async getAllOrders(
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId, @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId,
@Query('accounts') filterByAccounts?: string, @Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string, @Query('assetClasses') filterByAssetClasses?: string,
@Query('dataSource') filterByDataSource?: string,
@Query('range') dateRange?: DateRange, @Query('range') dateRange?: DateRange,
@Query('skip') skip?: number, @Query('skip') skip?: number,
@Query('sortColumn') sortColumn?: string, @Query('sortColumn') sortColumn?: string,
@Query('sortDirection') sortDirection?: Prisma.SortOrder, @Query('sortDirection') sortDirection?: Prisma.SortOrder,
@Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string, @Query('tags') filterByTags?: string,
@Query('take') take?: number @Query('take') take?: number
): Promise<Activities> { ): Promise<Activities> {
@ -116,6 +119,8 @@ export class OrderController {
const filters = this.apiService.buildFiltersFromQueryParams({ const filters = this.apiService.buildFiltersFromQueryParams({
filterByAccounts, filterByAccounts,
filterByAssetClasses, filterByAssetClasses,
filterByDataSource,
filterBySymbol,
filterByTags filterByTags
}); });

31
apps/api/src/app/order/order.service.ts

@ -350,6 +350,14 @@ export class OrderService {
return type; 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 }) => { const searchQuery = filters?.find(({ type }) => {
return type === 'SEARCH_QUERY'; return type === 'SEARCH_QUERY';
})?.id; })?.id;
@ -395,6 +403,29 @@ export class OrderService {
}; };
} }
if (filterByDataSource && filterBySymbol) {
if (where.SymbolProfile) {
where.SymbolProfile = {
AND: [
where.SymbolProfile,
{
AND: [
{ dataSource: <DataSource>filterByDataSource },
{ symbol: filterBySymbol }
]
}
]
};
} else {
where.SymbolProfile = {
AND: [
{ dataSource: <DataSource>filterByDataSource },
{ symbol: filterBySymbol }
]
};
}
}
if (searchQuery) { if (searchQuery) {
const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [ const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [
{ id: { mode: 'insensitive', startsWith: searchQuery } }, { id: { mode: 'insensitive', startsWith: searchQuery } },

10
apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts

@ -39,12 +39,12 @@ export class TransformDataSourceInRequestInterceptor<T>
}); });
} }
if (request.body.dataSource && !DataSource[request.body.dataSource]) { for (const type of ['body', 'params', 'query']) {
request.body.dataSource = decodeDataSource(request.body.dataSource); const dataSourceValue = request[type]?.dataSource;
}
if (request.params.dataSource && !DataSource[request.params.dataSource]) { if (dataSourceValue && !DataSource[dataSourceValue]) {
request.params.dataSource = decodeDataSource(request.params.dataSource); request[type].dataSource = decodeDataSource(dataSourceValue);
}
} }
} }

20
apps/api/src/services/api/api.service.ts

@ -10,22 +10,28 @@ export class ApiService {
filterByAccounts, filterByAccounts,
filterByAssetClasses, filterByAssetClasses,
filterByAssetSubClasses, filterByAssetSubClasses,
filterByDataSource,
filterByHoldingType, filterByHoldingType,
filterBySearchQuery, filterBySearchQuery,
filterBySymbol,
filterByTags filterByTags
}: { }: {
filterByAccounts?: string; filterByAccounts?: string;
filterByAssetClasses?: string; filterByAssetClasses?: string;
filterByAssetSubClasses?: string; filterByAssetSubClasses?: string;
filterByDataSource?: string;
filterByHoldingType?: string; filterByHoldingType?: string;
filterBySearchQuery?: string; filterBySearchQuery?: string;
filterBySymbol?: string;
filterByTags?: string; filterByTags?: string;
}): Filter[] { }): Filter[] {
const accountIds = filterByAccounts?.split(',') ?? []; const accountIds = filterByAccounts?.split(',') ?? [];
const assetClasses = filterByAssetClasses?.split(',') ?? []; const assetClasses = filterByAssetClasses?.split(',') ?? [];
const assetSubClasses = filterByAssetSubClasses?.split(',') ?? []; const assetSubClasses = filterByAssetSubClasses?.split(',') ?? [];
const dataSource = filterByDataSource;
const holdingType = filterByHoldingType; const holdingType = filterByHoldingType;
const searchQuery = filterBySearchQuery?.toLowerCase(); const searchQuery = filterBySearchQuery?.toLowerCase();
const symbol = filterBySymbol;
const tagIds = filterByTags?.split(',') ?? []; const tagIds = filterByTags?.split(',') ?? [];
const filters = [ const filters = [
@ -55,6 +61,13 @@ export class ApiService {
}) })
]; ];
if (dataSource) {
filters.push({
id: dataSource,
type: 'DATA_SOURCE'
});
}
if (holdingType) { if (holdingType) {
filters.push({ filters.push({
id: holdingType, id: holdingType,
@ -69,6 +82,13 @@ export class ApiService {
}); });
} }
if (symbol) {
filters.push({
id: symbol,
type: 'SYMBOL'
});
}
return filters; return filters;
} }
} }

20
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 activityForm: FormGroup;
public accounts: Account[]; public accounts: Account[];
public activities: Activity[];
public assetClass: string; public assetClass: string;
public assetSubClass: string; public assetSubClass: string;
public averagePrice: number; public averagePrice: number;
@ -174,6 +173,22 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
.subscribe(); .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 this.dataService
.fetchHoldingDetail({ .fetchHoldingDetail({
dataSource: this.data.dataSource, dataSource: this.data.dataSource,
@ -198,7 +213,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
netPerformancePercent, netPerformancePercent,
netPerformancePercentWithCurrencyEffect, netPerformancePercentWithCurrencyEffect,
netPerformanceWithCurrencyEffect, netPerformanceWithCurrencyEffect,
orders,
quantity, quantity,
SymbolProfile, SymbolProfile,
tags, tags,
@ -206,12 +220,10 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
value value
}) => { }) => {
this.accounts = accounts; this.accounts = accounts;
this.activities = orders;
this.averagePrice = averagePrice; this.averagePrice = averagePrice;
this.benchmarkDataItems = []; this.benchmarkDataItems = [];
this.countries = {}; this.countries = {};
this.dataProviderInfo = dataProviderInfo; this.dataProviderInfo = dataProviderInfo;
this.dataSource = new MatTableDataSource(orders.reverse());
this.dividendInBaseCurrency = dividendInBaseCurrency; this.dividendInBaseCurrency = dividendInBaseCurrency;
if ( if (

5
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html

@ -311,7 +311,7 @@
animationDuration="0" animationDuration="0"
class="mb-5" class="mb-5"
[mat-stretch-tabs]="false" [mat-stretch-tabs]="false"
[ngClass]="{ 'd-none': !activities?.length }" [ngClass]="{ 'd-none': !dataSource?.data.length }"
> >
<mat-tab> <mat-tab>
<ng-template mat-tab-label> <ng-template mat-tab-label>
@ -422,7 +422,8 @@
} }
@if ( @if (
activities?.length > 0 && data.hasPermissionToReportDataGlitch === true dataSource?.data.length > 0 &&
data.hasPermissionToReportDataGlitch === true
) { ) {
<div class="row"> <div class="row">
<div class="col"> <div class="col">

10
apps/client/src/app/services/data.service.ts

@ -72,14 +72,24 @@ export class DataService {
ACCOUNT: filtersByAccount, ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass, ASSET_CLASS: filtersByAssetClass,
ASSET_SUB_CLASS: filtersByAssetSubClass, ASSET_SUB_CLASS: filtersByAssetSubClass,
DATA_SOURCE: [filterByDataSource],
HOLDING_TYPE: filtersByHoldingType, HOLDING_TYPE: filtersByHoldingType,
PRESET_ID: filtersByPresetId, PRESET_ID: filtersByPresetId,
SEARCH_QUERY: filtersBySearchQuery, SEARCH_QUERY: filtersBySearchQuery,
SYMBOL: [filterBySymbol],
TAG: filtersByTag TAG: filtersByTag
} = groupBy(filters, (filter) => { } = groupBy(filters, (filter) => {
return filter.type; return filter.type;
}); });
if (filterByDataSource) {
params = params.append('dataSource', filterByDataSource.id);
}
if (filterBySymbol) {
params = params.append('symbol', filterBySymbol.id);
}
if (filtersByAccount) { if (filtersByAccount) {
params = params.append( params = params.append(
'accounts', 'accounts',

1
libs/common/src/lib/interfaces/filter.interface.ts

@ -5,6 +5,7 @@ export interface Filter {
| 'ACCOUNT' | 'ACCOUNT'
| 'ASSET_CLASS' | 'ASSET_CLASS'
| 'ASSET_SUB_CLASS' | 'ASSET_SUB_CLASS'
| 'DATA_SOURCE'
| 'HOLDING_TYPE' | 'HOLDING_TYPE'
| 'PRESET_ID' | 'PRESET_ID'
| 'SEARCH_QUERY' | 'SEARCH_QUERY'

Loading…
Cancel
Save