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>
This commit is contained in:
parent
8735fc3fad
commit
323cfbfcaa
10
CHANGELOG.md
10
CHANGELOG.md
@ -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;
|
||||
|
||||
if (accountFilter) {
|
||||
where.id = accountFilter.id;
|
||||
const filterByDataSource = filters?.find(({ type }) => {
|
||||
return type === 'DATA_SOURCE';
|
||||
})?.id;
|
||||
|
||||
const filterBySymbol = filters?.find(({ type }) => {
|
||||
return type === 'SYMBOL';
|
||||
})?.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…
x
Reference in New Issue
Block a user