Browse Source

Task/harmonize filter parsing using groupBy() (#7211)

* Harmonize filter parsing using groupBy()

* Update changelog
pull/7212/head^2
Thomas Kaul 2 weeks ago
committed by GitHub
parent
commit
6df2f4e07f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 9
      apps/api/src/app/account-balance/account-balance.service.ts
  3. 4
      apps/api/src/app/account/account.service.ts
  4. 45
      apps/api/src/app/activities/activities.service.ts
  5. 27
      apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts
  6. 4
      apps/api/src/app/export/export.service.ts
  7. 51
      apps/api/src/app/portfolio/portfolio.service.ts
  8. 38
      libs/ui/src/lib/services/data.service.ts

1
CHANGELOG.md

@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the parsing of integer query parameters (`skip` and `take`) in the `GET api/v1/asset-profiles` endpoint
- Improved the parsing of the integer query parameter (`includeHistoricalData`) in the `GET api/v1/market-data/markets` endpoint
- Improved the parsing of the integer query parameter (`includeHistoricalData`) in the `GET api/v1/symbol/:dataSource/:symbol` endpoint
- Harmonized the filter parsing using `groupBy` across various services
- Improved the language localization by translating various tooltips across the application
- Improved the language localization for Ukrainian (`uk`)
- Upgraded `yahoo-finance2` from version `3.14.3` to `3.15.4`

9
apps/api/src/app/account-balance/account-balance.service.ts

@ -15,6 +15,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { AccountBalance, Prisma } from '@prisma/client';
import { Big } from 'big.js';
import { format, parseISO } from 'date-fns';
import { groupBy } from 'lodash';
@Injectable()
export class AccountBalanceService {
@ -144,12 +145,12 @@ export class AccountBalanceService {
}): Promise<AccountBalancesResponse> {
const where: Prisma.AccountBalanceWhereInput = { userId };
const accountFilter = filters?.find(({ type }) => {
return type === 'ACCOUNT';
const { ACCOUNT: [filterByAccount] = [] } = groupBy(filters, ({ type }) => {
return type;
});
if (accountFilter) {
where.accountId = accountFilter.id;
if (filterByAccount) {
where.accountId = filterByAccount.id;
}
if (withExcludedAccounts === false) {

4
apps/api/src/app/account/account.service.ts

@ -187,11 +187,11 @@ export class AccountService {
where.isExcluded = false;
}
const { ACCOUNT: filtersByAccount } = groupBy(filters, ({ type }) => {
const { ACCOUNT: filtersByAccount = [] } = groupBy(filters, ({ type }) => {
return type;
});
if (filtersByAccount?.length > 0) {
if (filtersByAccount.length > 0) {
where.id = {
in: filtersByAccount.map(({ id }) => {
return id;

45
apps/api/src/app/activities/activities.service.ts

@ -556,26 +556,17 @@ export class ActivitiesService {
}
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
TAG: filtersByTag
ACCOUNT: filtersByAccount = [],
ASSET_CLASS: filtersByAssetClass = [],
DATA_SOURCE: [filterByDataSource] = [],
SEARCH_QUERY: [filterBySearchQuery] = [],
SYMBOL: [filterBySymbol] = [],
TAG: filtersByTag = []
} = groupBy(filters, ({ 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 }) => {
return type === 'SEARCH_QUERY';
})?.id;
if (filtersByAccount?.length > 0) {
if (filtersByAccount.length > 0) {
where.accountId = {
in: filtersByAccount.map(({ id }) => {
return id;
@ -587,7 +578,7 @@ export class ActivitiesService {
where.isDraft = false;
}
if (filtersByAssetClass?.length > 0) {
if (filtersByAssetClass.length > 0) {
where.SymbolProfile = {
OR: [
{
@ -623,8 +614,8 @@ export class ActivitiesService {
where.SymbolProfile,
{
AND: [
{ dataSource: filterByDataSource as DataSource },
{ symbol: filterBySymbol }
{ dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol.id }
]
}
]
@ -632,19 +623,19 @@ export class ActivitiesService {
} else {
where.SymbolProfile = {
AND: [
{ dataSource: filterByDataSource as DataSource },
{ symbol: filterBySymbol }
{ dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol.id }
]
};
}
}
if (searchQuery) {
if (filterBySearchQuery) {
const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [
{ id: { mode: 'insensitive', startsWith: searchQuery } },
{ isin: { mode: 'insensitive', startsWith: searchQuery } },
{ name: { mode: 'insensitive', startsWith: searchQuery } },
{ symbol: { mode: 'insensitive', startsWith: searchQuery } }
{ id: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ isin: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ name: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ symbol: { mode: 'insensitive', startsWith: filterBySearchQuery.id } }
];
if (where.SymbolProfile) {
@ -663,7 +654,7 @@ export class ActivitiesService {
}
}
if (filtersByTag?.length > 0) {
if (filtersByTag.length > 0) {
where.tags = {
some: {
OR: filtersByTag.map(({ id }) => {

27
apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts

@ -133,13 +133,10 @@ export class AssetProfilesService {
};
}
const searchQuery = filters.find(({ type }) => {
return type === 'SEARCH_QUERY';
})?.id;
const {
ASSET_SUB_CLASS: filtersByAssetSubClass,
DATA_SOURCE: filtersByDataSource
ASSET_SUB_CLASS: [filterByAssetSubClass] = [],
DATA_SOURCE: [filterByDataSource] = [],
SEARCH_QUERY: [filterBySearchQuery] = []
} = groupBy(filters, ({ type }) => {
return type;
});
@ -149,20 +146,20 @@ export class AssetProfilesService {
by: ['dataSource', 'symbol']
});
if (filtersByAssetSubClass) {
where.assetSubClass = AssetSubClass[filtersByAssetSubClass[0].id];
if (filterByAssetSubClass) {
where.assetSubClass = AssetSubClass[filterByAssetSubClass.id];
}
if (filtersByDataSource) {
where.dataSource = DataSource[filtersByDataSource[0].id];
if (filterByDataSource) {
where.dataSource = DataSource[filterByDataSource.id];
}
if (searchQuery) {
if (filterBySearchQuery) {
where.OR = [
{ id: { mode: 'insensitive', startsWith: searchQuery } },
{ isin: { mode: 'insensitive', startsWith: searchQuery } },
{ name: { mode: 'insensitive', startsWith: searchQuery } },
{ symbol: { mode: 'insensitive', startsWith: searchQuery } }
{ id: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ isin: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ name: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ symbol: { mode: 'insensitive', startsWith: filterBySearchQuery.id } }
];
}

4
apps/api/src/app/export/export.service.ts

@ -35,7 +35,7 @@ export class ExportService {
userId: string;
userSettings: UserSettings;
}): Promise<ExportResponse> {
const { ACCOUNT: filtersByAccount } = groupBy(filters, ({ type }) => {
const { ACCOUNT: filtersByAccount = [] } = groupBy(filters, ({ type }) => {
return type;
});
const platformsMap: { [platformId: string]: Platform } = {};
@ -59,7 +59,7 @@ export class ExportService {
const where: Prisma.AccountWhereInput = { userId };
if (filtersByAccount?.length > 0) {
if (filtersByAccount.length > 0) {
where.id = {
in: filtersByAccount.map(({ id }) => {
return id;

51
apps/api/src/app/portfolio/portfolio.service.ts

@ -95,6 +95,7 @@ import {
parseISO,
set
} from 'date-fns';
import { groupBy } from 'lodash';
import { PortfolioCalculator } from './calculator/portfolio-calculator';
import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory';
@ -138,20 +139,16 @@ export class PortfolioService {
}): Promise<AccountWithValue[]> {
const where: Prisma.AccountWhereInput = { userId };
const filterByAccount = filters?.find(({ type }) => {
return type === 'ACCOUNT';
})?.id;
const filterByDataSource = filters?.find(({ type }) => {
return type === 'DATA_SOURCE';
})?.id;
const filterBySymbol = filters?.find(({ type }) => {
return type === 'SYMBOL';
})?.id;
const {
ACCOUNT: [filterByAccount] = [],
DATA_SOURCE: [filterByDataSource] = [],
SYMBOL: [filterBySymbol] = []
} = groupBy(filters, ({ type }) => {
return type;
});
if (filterByAccount) {
where.id = filterByAccount;
where.id = filterByAccount.id;
}
if (filterByDataSource && filterBySymbol) {
@ -159,8 +156,8 @@ export class PortfolioService {
some: {
SymbolProfile: {
AND: [
{ dataSource: filterByDataSource as DataSource },
{ symbol: filterBySymbol }
{ dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol.id }
]
}
}
@ -274,17 +271,20 @@ export class PortfolioService {
let activitiesCount = 0;
const searchQuery = filters.find(({ type }) => {
return type === 'SEARCH_QUERY';
})?.id;
const { SEARCH_QUERY: [filterBySearchQuery] = [] } = groupBy(
filters,
({ type }) => {
return type;
}
);
if (searchQuery) {
if (filterBySearchQuery) {
const fuse = new Fuse(accounts, {
keys: ['name', 'platform.name'],
threshold: 0.3
});
accounts = fuse.search(searchQuery).map(({ item }) => {
accounts = fuse.search(filterBySearchQuery.id).map(({ item }) => {
return item;
});
}
@ -376,17 +376,20 @@ export class PortfolioService {
let holdings = Object.values(holdingsMap);
const searchQuery = filters.find(({ type }) => {
return type === 'SEARCH_QUERY';
})?.id;
const { SEARCH_QUERY: [filterBySearchQuery] = [] } = groupBy(
filters,
({ type }) => {
return type;
}
);
if (searchQuery) {
if (filterBySearchQuery) {
const fuse = new Fuse(holdings, {
keys: ['isin', 'name', 'symbol'],
threshold: 0.3
});
holdings = fuse.search(searchQuery).map(({ item }) => {
holdings = fuse.search(filterBySearchQuery.id).map(({ item }) => {
return item;
});
}

38
libs/ui/src/lib/services/data.service.ts

@ -96,17 +96,17 @@ export class DataService {
if (filters && filters.length > 0) {
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
ASSET_SUB_CLASS: filtersByAssetSubClass,
ACCOUNT: filtersByAccount = [],
ASSET_CLASS: filtersByAssetClass = [],
ASSET_SUB_CLASS: filtersByAssetSubClass = [],
DATA_SOURCE: [filterByDataSource] = [],
HOLDING_TYPE: filtersByHoldingType,
PRESET_ID: filtersByPresetId,
SEARCH_QUERY: filtersBySearchQuery,
HOLDING_TYPE: [filterByHoldingType] = [],
PRESET_ID: [filterByPresetId] = [],
SEARCH_QUERY: [filterBySearchQuery] = [],
SYMBOL: [filterBySymbol] = [],
TAG: filtersByTag
} = groupBy(filters, (filter) => {
return filter.type;
TAG: filtersByTag = []
} = groupBy(filters, ({ type }) => {
return type;
});
if (filterByDataSource) {
@ -117,7 +117,7 @@ export class DataService {
params = params.append('symbol', filterBySymbol.id);
}
if (filtersByAccount) {
if (filtersByAccount.length > 0) {
params = params.append(
'accounts',
filtersByAccount
@ -128,7 +128,7 @@ export class DataService {
);
}
if (filtersByAssetClass) {
if (filtersByAssetClass.length > 0) {
params = params.append(
'assetClasses',
filtersByAssetClass
@ -139,7 +139,7 @@ export class DataService {
);
}
if (filtersByAssetSubClass) {
if (filtersByAssetSubClass.length > 0) {
params = params.append(
'assetSubClasses',
filtersByAssetSubClass
@ -150,19 +150,19 @@ export class DataService {
);
}
if (filtersByHoldingType) {
params = params.append('holdingType', filtersByHoldingType[0].id);
if (filterByHoldingType) {
params = params.append('holdingType', filterByHoldingType.id);
}
if (filtersByPresetId) {
params = params.append('presetId', filtersByPresetId[0].id);
if (filterByPresetId) {
params = params.append('presetId', filterByPresetId.id);
}
if (filtersBySearchQuery) {
params = params.append('query', filtersBySearchQuery[0].id);
if (filterBySearchQuery) {
params = params.append('query', filterBySearchQuery.id);
}
if (filtersByTag) {
if (filtersByTag.length > 0) {
params = params.append(
'tags',
filtersByTag

Loading…
Cancel
Save