Feature/deprecate portfolio position endpoints (#4648)
* Deprecate api/v1/portfolio/position endpoints * Update changelog
This commit is contained in:
parent
3e963228d6
commit
1bced96460
@ -5,6 +5,13 @@ 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
|
||||
|
||||
### Changed
|
||||
|
||||
- Deprecated the endpoint to get a portfolio position in favor of get a holding
|
||||
- Deprecated the endpoint to update portfolio position tags in favor of update holding tags
|
||||
|
||||
## 2.159.0 - 2025-05-02
|
||||
|
||||
### Added
|
||||
|
@ -50,7 +50,7 @@ export class ImportService {
|
||||
}: AssetProfileIdentifier): Promise<Activity[]> {
|
||||
try {
|
||||
const { firstBuyDate, historicalData, orders } =
|
||||
await this.portfolioService.getPosition(dataSource, undefined, symbol);
|
||||
await this.portfolioService.getHolding(dataSource, undefined, symbol);
|
||||
|
||||
const [[assetProfile], dividends] = await Promise.all([
|
||||
this.symbolProfileService.getSymbolProfiles([
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
import {
|
||||
PortfolioDetails,
|
||||
PortfolioDividends,
|
||||
PortfolioHoldingResponse,
|
||||
PortfolioHoldingsResponse,
|
||||
PortfolioInvestments,
|
||||
PortfolioPerformanceResponse,
|
||||
@ -56,7 +57,6 @@ import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
|
||||
import { Big } from 'big.js';
|
||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||
|
||||
import { PortfolioHoldingDetail } from './interfaces/portfolio-holding-detail.interface';
|
||||
import { PortfolioService } from './portfolio.service';
|
||||
import { UpdateHoldingTagsDto } from './update-holding-tags.dto';
|
||||
|
||||
@ -365,6 +365,32 @@ export class PortfolioController {
|
||||
return { dividends };
|
||||
}
|
||||
|
||||
@Get('holding/:dataSource/:symbol')
|
||||
@UseInterceptors(RedactValuesInResponseInterceptor)
|
||||
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
||||
@UseInterceptors(TransformDataSourceInResponseInterceptor)
|
||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||
public async getHolding(
|
||||
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string,
|
||||
@Param('dataSource') dataSource: DataSource,
|
||||
@Param('symbol') symbol: string
|
||||
): Promise<PortfolioHoldingResponse> {
|
||||
const holding = await this.portfolioService.getHolding(
|
||||
dataSource,
|
||||
impersonationId,
|
||||
symbol
|
||||
);
|
||||
|
||||
if (!holding) {
|
||||
throw new HttpException(
|
||||
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||
StatusCodes.NOT_FOUND
|
||||
);
|
||||
}
|
||||
|
||||
return holding;
|
||||
}
|
||||
|
||||
@Get('holdings')
|
||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||
@UseInterceptors(RedactValuesInResponseInterceptor)
|
||||
@ -583,6 +609,9 @@ export class PortfolioController {
|
||||
return performanceInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Get('position/:dataSource/:symbol')
|
||||
@UseInterceptors(RedactValuesInResponseInterceptor)
|
||||
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
||||
@ -592,8 +621,8 @@ export class PortfolioController {
|
||||
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string,
|
||||
@Param('dataSource') dataSource: DataSource,
|
||||
@Param('symbol') symbol: string
|
||||
): Promise<PortfolioHoldingDetail> {
|
||||
const holding = await this.portfolioService.getPosition(
|
||||
): Promise<PortfolioHoldingResponse> {
|
||||
const holding = await this.portfolioService.getHolding(
|
||||
dataSource,
|
||||
impersonationId,
|
||||
symbol
|
||||
@ -634,7 +663,7 @@ export class PortfolioController {
|
||||
}
|
||||
|
||||
@HasPermission(permissions.updateOrder)
|
||||
@Put('position/:dataSource/:symbol/tags')
|
||||
@Put('holding/:dataSource/:symbol/tags')
|
||||
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||
public async updateHoldingTags(
|
||||
@ -643,7 +672,42 @@ export class PortfolioController {
|
||||
@Param('dataSource') dataSource: DataSource,
|
||||
@Param('symbol') symbol: string
|
||||
): Promise<void> {
|
||||
const holding = await this.portfolioService.getPosition(
|
||||
const holding = await this.portfolioService.getHolding(
|
||||
dataSource,
|
||||
impersonationId,
|
||||
symbol
|
||||
);
|
||||
|
||||
if (!holding) {
|
||||
throw new HttpException(
|
||||
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||
StatusCodes.NOT_FOUND
|
||||
);
|
||||
}
|
||||
|
||||
await this.portfolioService.updateTags({
|
||||
dataSource,
|
||||
impersonationId,
|
||||
symbol,
|
||||
tags: data.tags,
|
||||
userId: this.request.user.id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@HasPermission(permissions.updateOrder)
|
||||
@Put('position/:dataSource/:symbol/tags')
|
||||
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||
public async updatePositionTags(
|
||||
@Body() data: UpdateHoldingTagsDto,
|
||||
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string,
|
||||
@Param('dataSource') dataSource: DataSource,
|
||||
@Param('symbol') symbol: string
|
||||
): Promise<void> {
|
||||
const holding = await this.portfolioService.getHolding(
|
||||
dataSource,
|
||||
impersonationId,
|
||||
symbol
|
||||
|
@ -41,6 +41,7 @@ import {
|
||||
HistoricalDataItem,
|
||||
InvestmentItem,
|
||||
PortfolioDetails,
|
||||
PortfolioHoldingResponse,
|
||||
PortfolioInvestments,
|
||||
PortfolioPerformanceResponse,
|
||||
PortfolioPosition,
|
||||
@ -87,7 +88,6 @@ import { isEmpty } from 'lodash';
|
||||
|
||||
import { PortfolioCalculator } from './calculator/portfolio-calculator';
|
||||
import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory';
|
||||
import { PortfolioHoldingDetail } from './interfaces/portfolio-holding-detail.interface';
|
||||
import { RulesService } from './rules.service';
|
||||
|
||||
const asiaPacificMarkets = require('../../assets/countries/asia-pacific-markets.json');
|
||||
@ -631,11 +631,11 @@ export class PortfolioService {
|
||||
};
|
||||
}
|
||||
|
||||
public async getPosition(
|
||||
public async getHolding(
|
||||
aDataSource: DataSource,
|
||||
aImpersonationId: string,
|
||||
aSymbol: string
|
||||
): Promise<PortfolioHoldingDetail> {
|
||||
): Promise<PortfolioHoldingResponse> {
|
||||
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
|
||||
const user = await this.userService.user({ id: userId });
|
||||
const userCurrency = this.getUserCurrency(user);
|
||||
@ -927,7 +927,7 @@ export class PortfolioService {
|
||||
}
|
||||
}
|
||||
|
||||
public async getPositions({
|
||||
public async getHoldings({
|
||||
dateRange = 'max',
|
||||
filters,
|
||||
impersonationId
|
||||
|
@ -13,7 +13,6 @@ import {
|
||||
Activity
|
||||
} from '@ghostfolio/api/app/order/interfaces/activities.interface';
|
||||
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
|
||||
import { PortfolioHoldingDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-holding-detail.interface';
|
||||
import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface';
|
||||
import { DeleteOwnUserDto } from '@ghostfolio/api/app/user/delete-own-user.dto';
|
||||
import { UserItem } from '@ghostfolio/api/app/user/interfaces/user-item.interface';
|
||||
@ -40,6 +39,7 @@ import {
|
||||
OAuthResponse,
|
||||
PortfolioDetails,
|
||||
PortfolioDividends,
|
||||
PortfolioHoldingResponse,
|
||||
PortfolioHoldingsResponse,
|
||||
PortfolioInvestments,
|
||||
PortfolioPerformanceResponse,
|
||||
@ -406,8 +406,8 @@ export class DataService {
|
||||
symbol: string;
|
||||
}) {
|
||||
return this.http
|
||||
.get<PortfolioHoldingDetail>(
|
||||
`/api/v1/portfolio/position/${dataSource}/${symbol}`
|
||||
.get<PortfolioHoldingResponse>(
|
||||
`/api/v1/portfolio/holding/${dataSource}/${symbol}`
|
||||
)
|
||||
.pipe(
|
||||
map((data) => {
|
||||
@ -776,7 +776,7 @@ export class DataService {
|
||||
tags
|
||||
}: { tags: Tag[] } & AssetProfileIdentifier) {
|
||||
return this.http.put<void>(
|
||||
`/api/v1/portfolio/position/${dataSource}/${symbol}/tags`,
|
||||
`/api/v1/portfolio/holding/${dataSource}/${symbol}/tags`,
|
||||
{ tags }
|
||||
);
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ import type { ImportResponse } from './responses/import-response.interface';
|
||||
import type { LookupResponse } from './responses/lookup-response.interface';
|
||||
import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface';
|
||||
import type { OAuthResponse } from './responses/oauth-response.interface';
|
||||
import { PortfolioHoldingResponse } from './responses/portfolio-holding-response.interface';
|
||||
import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface';
|
||||
import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface';
|
||||
import type { PortfolioReportResponse } from './responses/portfolio-report.interface';
|
||||
@ -112,6 +113,7 @@ export {
|
||||
PortfolioChart,
|
||||
PortfolioDetails,
|
||||
PortfolioDividends,
|
||||
PortfolioHoldingResponse,
|
||||
PortfolioHoldingsResponse,
|
||||
PortfolioInvestments,
|
||||
PortfolioItem,
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
|
||||
import { Tag } from '@prisma/client';
|
||||
|
||||
export interface PortfolioHoldingDetail {
|
||||
export interface PortfolioHoldingResponse {
|
||||
averagePrice: number;
|
||||
dataProviderInfo: DataProviderInfo;
|
||||
dividendInBaseCurrency: number;
|
Loading…
x
Reference in New Issue
Block a user