Feature/introduce filters in account endpoint (#3764)

* Introduce filters in acount endpoint

* Integrate endpoint in holding detail dialog

* Update changelog

---------

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
pull/3765/head
Shaunak Das 3 months ago committed by GitHub
parent 8735fc3fad
commit 323cfbfcaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Introduced filters (`dataSource` and `symbol`) in the accounts endpoint
### Changed
- Switched to the accounts endpoint in the holding detail dialog
## 2.107.1 - 2024-09-12
### Fixed

@ -3,6 +3,8 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.servic
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { RedactValuesInResponseInterceptor } from '@ghostfolio/api/interceptors/redact-values-in-response/redact-values-in-response.interceptor';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor';
import { ApiService } from '@ghostfolio/api/services/api/api.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config';
import {
@ -26,6 +28,7 @@ import {
Param,
Post,
Put,
Query,
UseGuards,
UseInterceptors
} from '@nestjs/common';
@ -44,6 +47,7 @@ export class AccountController {
public constructor(
private readonly accountBalanceService: AccountBalanceService,
private readonly accountService: AccountService,
private readonly apiService: ApiService,
private readonly impersonationService: ImpersonationService,
private readonly portfolioService: PortfolioService,
@Inject(REQUEST) private readonly request: RequestWithUser
@ -84,13 +88,22 @@ export class AccountController {
@Get()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor)
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async getAllAccounts(
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId,
@Query('dataSource') filterByDataSource?: string,
@Query('symbol') filterBySymbol?: string
): Promise<Accounts> {
const impersonationUserId =
await this.impersonationService.validateImpersonationId(impersonationId);
const filters = this.apiService.buildFiltersFromQueryParams({
filterByDataSource,
filterBySymbol
});
return this.portfolioService.getAccountsWithAggregations({
filters,
userId: impersonationUserId || this.request.user.id,
withExcludedAccounts: true
});

@ -1,6 +1,7 @@
import { AccountBalanceModule } from '@ghostfolio/api/app/account-balance/account-balance.module';
import { PortfolioModule } from '@ghostfolio/api/app/portfolio/portfolio.module';
import { RedactValuesInResponseModule } from '@ghostfolio/api/interceptors/redact-values-in-response/redact-values-in-response.module';
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module';
@ -16,6 +17,7 @@ import { AccountService } from './account.service';
exports: [AccountService],
imports: [
AccountBalanceModule,
ApiModule,
ConfigurationModule,
ExchangeRateDataModule,
ImpersonationModule,

@ -115,12 +115,33 @@ export class PortfolioService {
}): Promise<AccountWithValue[]> {
const where: Prisma.AccountWhereInput = { userId };
const accountFilter = filters?.find(({ type }) => {
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;
if (accountFilter) {
where.id = accountFilter.id;
if (filterByAccount) {
where.id = filterByAccount;
}
if (filterByDataSource && filterBySymbol) {
where.Order = {
some: {
SymbolProfile: {
AND: [
{ dataSource: <DataSource>filterByDataSource },
{ symbol: filterBySymbol }
]
}
}
};
}
const [accounts, details] = await Promise.all([

@ -9,6 +9,7 @@ import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
import {
DataProviderInfo,
EnhancedSymbolProfile,
Filter,
LineChartItem,
User
} from '@ghostfolio/common/interfaces';
@ -152,6 +153,11 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
tags: <string[]>[]
});
const filters: Filter[] = [
{ id: this.data.dataSource, type: 'DATA_SOURCE' },
{ id: this.data.symbol, type: 'SYMBOL' }
];
this.tagsAvailable = tags.map(({ id, name }) => {
return {
id,
@ -173,12 +179,20 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
.subscribe();
});
this.dataService
.fetchAccounts({
filters
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ accounts }) => {
this.accounts = accounts;
this.changeDetectorRef.markForCheck();
});
this.dataService
.fetchActivities({
filters: [
{ id: this.data.dataSource, type: 'DATA_SOURCE' },
{ id: this.data.symbol, type: 'SYMBOL' }
],
filters,
sortColumn: this.sortColumn,
sortDirection: this.sortDirection
})
@ -197,7 +211,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(
({
accounts,
averagePrice,
dataProviderInfo,
dividendInBaseCurrency,
@ -219,7 +232,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
transactionCount,
value
}) => {
this.accounts = accounts;
this.averagePrice = averagePrice;
this.benchmarkDataItems = [];
this.countries = {};

@ -173,8 +173,10 @@ export class DataService {
);
}
public fetchAccounts() {
return this.http.get<Accounts>('/api/v1/account');
public fetchAccounts({ filters }: { filters?: Filter[] } = {}) {
const params = this.buildFiltersAsQueryParams({ filters });
return this.http.get<Accounts>('/api/v1/account', { params });
}
public fetchActivities({

Loading…
Cancel
Save