Feature/add support for search query in portfolio position endpoint (#2443)

* Introduce search query filter

* Update changelog
pull/2429/head^2
Thomas Kaul 1 year ago committed by GitHub
parent 2a71cb66de
commit 8236091477
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Added support for notes in the activities import - Added support for notes in the activities import
- Added support for a search query in the portfolio position endpoint
- Added the application version to the endpoint `GET api/v1/admin` - Added the application version to the endpoint `GET api/v1/admin`
- Introduced a carousel component for the testimonial section on the landing page - Introduced a carousel component for the testimonial section on the landing page

@ -391,12 +391,14 @@ export class PortfolioController {
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string,
@Query('accounts') filterByAccounts?: string, @Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string, @Query('assetClasses') filterByAssetClasses?: string,
@Query('query') filterBySearchQuery?: string,
@Query('range') dateRange: DateRange = 'max', @Query('range') dateRange: DateRange = 'max',
@Query('tags') filterByTags?: string @Query('tags') filterByTags?: string
): Promise<PortfolioPositions> { ): Promise<PortfolioPositions> {
const filters = this.apiService.buildFiltersFromQueryParams({ const filters = this.apiService.buildFiltersFromQueryParams({
filterByAccounts, filterByAccounts,
filterByAssetClasses, filterByAssetClasses,
filterBySearchQuery,
filterByTags filterByTags
}); });

@ -1014,6 +1014,9 @@ export class PortfolioService {
filters?: Filter[]; filters?: Filter[];
impersonationId: string; impersonationId: string;
}): Promise<{ hasErrors: boolean; positions: Position[] }> { }): Promise<{ hasErrors: boolean; positions: Position[] }> {
const searchQuery = filters.find(({ type }) => {
return type === 'SEARCH_QUERY';
})?.id;
const userId = await this.getUserId(impersonationId, this.request.user.id); const userId = await this.getUserId(impersonationId, this.request.user.id);
const { portfolioOrders, transactionPoints } = const { portfolioOrders, transactionPoints } =
@ -1042,9 +1045,9 @@ export class PortfolioService {
const currentPositions = const currentPositions =
await portfolioCalculator.getCurrentPositions(startDate); await portfolioCalculator.getCurrentPositions(startDate);
const positions = currentPositions.positions.filter( let positions = currentPositions.positions.filter(({ quantity }) => {
(item) => !item.quantity.eq(0) return !quantity.eq(0);
); });
const dataGatheringItems = positions.map(({ dataSource, symbol }) => { const dataGatheringItems = positions.map(({ dataSource, symbol }) => {
return { return {
@ -1067,6 +1070,17 @@ export class PortfolioService {
symbolProfileMap[symbolProfile.symbol] = symbolProfile; symbolProfileMap[symbolProfile.symbol] = symbolProfile;
} }
if (searchQuery) {
positions = positions.filter(({ symbol }) => {
const enhancedSymbolProfile = symbolProfileMap[symbol];
return (
enhancedSymbolProfile.isin?.toLowerCase().startsWith(searchQuery) ||
enhancedSymbolProfile.name?.toLowerCase().startsWith(searchQuery)
);
});
}
return { return {
hasErrors: currentPositions.hasErrors, hasErrors: currentPositions.hasErrors,
positions: positions.map((position) => { positions: positions.map((position) => {

@ -8,14 +8,17 @@ export class ApiService {
public buildFiltersFromQueryParams({ public buildFiltersFromQueryParams({
filterByAccounts, filterByAccounts,
filterByAssetClasses, filterByAssetClasses,
filterBySearchQuery,
filterByTags filterByTags
}: { }: {
filterByAccounts?: string; filterByAccounts?: string;
filterByAssetClasses?: string; filterByAssetClasses?: string;
filterBySearchQuery?: string;
filterByTags?: string; filterByTags?: string;
}): Filter[] { }): Filter[] {
const accountIds = filterByAccounts?.split(',') ?? []; const accountIds = filterByAccounts?.split(',') ?? [];
const assetClasses = filterByAssetClasses?.split(',') ?? []; const assetClasses = filterByAssetClasses?.split(',') ?? [];
const searchQuery = filterBySearchQuery?.toLowerCase();
const tagIds = filterByTags?.split(',') ?? []; const tagIds = filterByTags?.split(',') ?? [];
return [ return [
@ -31,6 +34,10 @@ export class ApiService {
type: 'ASSET_CLASS' type: 'ASSET_CLASS'
}; };
}), }),
{
id: searchQuery,
type: 'SEARCH_QUERY'
},
...tagIds.map((tagId) => { ...tagIds.map((tagId) => {
return <Filter>{ return <Filter>{
id: tagId, id: tagId,

@ -15,6 +15,7 @@ export interface EnhancedSymbolProfile {
dataSource: DataSource; dataSource: DataSource;
dateOfFirstActivity?: Date; dateOfFirstActivity?: Date;
id: string; id: string;
isin: string | null;
name: string | null; name: string | null;
scraperConfiguration?: ScraperConfiguration | null; scraperConfiguration?: ScraperConfiguration | null;
sectors: Sector[]; sectors: Sector[];

@ -6,6 +6,7 @@ export interface Filter {
| 'ASSET_CLASS' | 'ASSET_CLASS'
| 'ASSET_SUB_CLASS' | 'ASSET_SUB_CLASS'
| 'PRESET_ID' | 'PRESET_ID'
| 'SEARCH_QUERY'
| 'SYMBOL' | 'SYMBOL'
| 'TAG'; | 'TAG';
} }

Loading…
Cancel
Save