Feature/move market data management from admin to dedicated endpoint (#4125)
* Move market data management from admin to dedicated endpoint * Update changelog
This commit is contained in:
parent
a776ea8864
commit
c3bd433ac9
@ -5,6 +5,12 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Extracted the market data management from the admin control panel endpoint to a dedicated endpoint
|
||||||
|
|
||||||
## 2.129.0 - 2024-12-14
|
## 2.129.0 - 2024-12-14
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -214,6 +214,9 @@ export class AdminController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
@Get('market-data/:dataSource/:symbol')
|
@Get('market-data/:dataSource/:symbol')
|
||||||
@HasPermission(permissions.accessAdminControl)
|
@HasPermission(permissions.accessAdminControl)
|
||||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||||
@ -250,6 +253,9 @@ export class AdminController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
@HasPermission(permissions.accessAdminControl)
|
@HasPermission(permissions.accessAdminControl)
|
||||||
@Post('market-data/:dataSource/:symbol')
|
@Post('market-data/:dataSource/:symbol')
|
||||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||||
|
@ -33,6 +33,7 @@ import { BenchmarkModule } from './benchmark/benchmark.module';
|
|||||||
import { CacheModule } from './cache/cache.module';
|
import { CacheModule } from './cache/cache.module';
|
||||||
import { ApiKeysModule } from './endpoints/api-keys/api-keys.module';
|
import { ApiKeysModule } from './endpoints/api-keys/api-keys.module';
|
||||||
import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module';
|
import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module';
|
||||||
|
import { MarketDataModule } from './endpoints/market-data/market-data.module';
|
||||||
import { PublicModule } from './endpoints/public/public.module';
|
import { PublicModule } from './endpoints/public/public.module';
|
||||||
import { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
|
import { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
|
||||||
import { ExportModule } from './export/export.module';
|
import { ExportModule } from './export/export.module';
|
||||||
@ -84,6 +85,7 @@ import { UserModule } from './user/user.module';
|
|||||||
ImportModule,
|
ImportModule,
|
||||||
InfoModule,
|
InfoModule,
|
||||||
LogoModule,
|
LogoModule,
|
||||||
|
MarketDataModule,
|
||||||
OrderModule,
|
OrderModule,
|
||||||
PlatformModule,
|
PlatformModule,
|
||||||
PortfolioModule,
|
PortfolioModule,
|
||||||
|
136
apps/api/src/app/endpoints/market-data/market-data.controller.ts
Normal file
136
apps/api/src/app/endpoints/market-data/market-data.controller.ts
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import { AdminService } from '@ghostfolio/api/app/admin/admin.service';
|
||||||
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
||||||
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||||
|
import { MarketDataDetailsResponse } from '@ghostfolio/common/interfaces';
|
||||||
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
|
import { RequestWithUser } from '@ghostfolio/common/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
HttpException,
|
||||||
|
Inject,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
UseGuards
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { REQUEST } from '@nestjs/core';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { DataSource, Prisma } from '@prisma/client';
|
||||||
|
import { parseISO } from 'date-fns';
|
||||||
|
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
|
||||||
|
|
||||||
|
import { UpdateBulkMarketDataDto } from './update-bulk-market-data.dto';
|
||||||
|
|
||||||
|
@Controller('market-data')
|
||||||
|
export class MarketDataController {
|
||||||
|
public constructor(
|
||||||
|
private readonly adminService: AdminService,
|
||||||
|
private readonly marketDataService: MarketDataService,
|
||||||
|
@Inject(REQUEST) private readonly request: RequestWithUser,
|
||||||
|
private readonly symbolProfileService: SymbolProfileService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get(':dataSource/:symbol')
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
|
public async getMarketDataBySymbol(
|
||||||
|
@Param('dataSource') dataSource: DataSource,
|
||||||
|
@Param('symbol') symbol: string
|
||||||
|
): Promise<MarketDataDetailsResponse> {
|
||||||
|
const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([
|
||||||
|
{ dataSource, symbol }
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!assetProfile) {
|
||||||
|
throw new HttpException(
|
||||||
|
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||||
|
StatusCodes.NOT_FOUND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const canReadAllAssetProfiles = hasPermission(
|
||||||
|
this.request.user.permissions,
|
||||||
|
permissions.readMarketData
|
||||||
|
);
|
||||||
|
|
||||||
|
const canReadOwnAssetProfile =
|
||||||
|
assetProfile.userId === this.request.user.id &&
|
||||||
|
hasPermission(
|
||||||
|
this.request.user.permissions,
|
||||||
|
permissions.readMarketDataOfOwnAssetProfile
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!canReadAllAssetProfiles && !canReadOwnAssetProfile) {
|
||||||
|
throw new HttpException(
|
||||||
|
assetProfile.userId
|
||||||
|
? getReasonPhrase(StatusCodes.NOT_FOUND)
|
||||||
|
: getReasonPhrase(StatusCodes.FORBIDDEN),
|
||||||
|
assetProfile.userId ? StatusCodes.NOT_FOUND : StatusCodes.FORBIDDEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.adminService.getMarketDataBySymbol({ dataSource, symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(':dataSource/:symbol')
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
|
public async updateMarketData(
|
||||||
|
@Body() data: UpdateBulkMarketDataDto,
|
||||||
|
@Param('dataSource') dataSource: DataSource,
|
||||||
|
@Param('symbol') symbol: string
|
||||||
|
) {
|
||||||
|
const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([
|
||||||
|
{ dataSource, symbol }
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!assetProfile) {
|
||||||
|
throw new HttpException(
|
||||||
|
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||||
|
StatusCodes.NOT_FOUND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const canUpsertAllAssetProfiles =
|
||||||
|
hasPermission(
|
||||||
|
this.request.user.permissions,
|
||||||
|
permissions.createMarketData
|
||||||
|
) &&
|
||||||
|
hasPermission(
|
||||||
|
this.request.user.permissions,
|
||||||
|
permissions.updateMarketData
|
||||||
|
);
|
||||||
|
|
||||||
|
const canUpsertOwnAssetProfile =
|
||||||
|
assetProfile.userId === this.request.user.id &&
|
||||||
|
hasPermission(
|
||||||
|
this.request.user.permissions,
|
||||||
|
permissions.createMarketDataOfOwnAssetProfile
|
||||||
|
) &&
|
||||||
|
hasPermission(
|
||||||
|
this.request.user.permissions,
|
||||||
|
permissions.updateMarketDataOfOwnAssetProfile
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!canUpsertAllAssetProfiles && !canUpsertOwnAssetProfile) {
|
||||||
|
throw new HttpException(
|
||||||
|
getReasonPhrase(StatusCodes.FORBIDDEN),
|
||||||
|
StatusCodes.FORBIDDEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataBulkUpdate: Prisma.MarketDataUpdateInput[] = data.marketData.map(
|
||||||
|
({ date, marketPrice }) => ({
|
||||||
|
dataSource,
|
||||||
|
marketPrice,
|
||||||
|
symbol,
|
||||||
|
date: parseISO(date),
|
||||||
|
state: 'CLOSE'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.marketDataService.updateMany({
|
||||||
|
data: dataBulkUpdate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
13
apps/api/src/app/endpoints/market-data/market-data.module.ts
Normal file
13
apps/api/src/app/endpoints/market-data/market-data.module.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { AdminModule } from '@ghostfolio/api/app/admin/admin.module';
|
||||||
|
import { MarketDataModule as MarketDataServiceModule } from '@ghostfolio/api/services/market-data/market-data.module';
|
||||||
|
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
|
||||||
|
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { MarketDataController } from './market-data.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [MarketDataController],
|
||||||
|
imports: [AdminModule, MarketDataServiceModule, SymbolProfileModule]
|
||||||
|
})
|
||||||
|
export class MarketDataModule {}
|
@ -0,0 +1,24 @@
|
|||||||
|
import { Type } from 'class-transformer';
|
||||||
|
import {
|
||||||
|
ArrayNotEmpty,
|
||||||
|
IsArray,
|
||||||
|
IsISO8601,
|
||||||
|
IsNumber,
|
||||||
|
IsOptional
|
||||||
|
} from 'class-validator';
|
||||||
|
|
||||||
|
export class UpdateBulkMarketDataDto {
|
||||||
|
@ArrayNotEmpty()
|
||||||
|
@IsArray()
|
||||||
|
@Type(() => UpdateMarketDataDto)
|
||||||
|
marketData: UpdateMarketDataDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateMarketDataDto {
|
||||||
|
@IsISO8601()
|
||||||
|
@IsOptional()
|
||||||
|
date?: string;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
marketPrice: number;
|
||||||
|
}
|
@ -121,8 +121,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.adminService
|
this.dataService
|
||||||
.fetchAdminMarketDataBySymbol({
|
.fetchMarketDataBySymbol({
|
||||||
dataSource: this.data.dataSource,
|
dataSource: this.data.dataSource,
|
||||||
symbol: this.data.symbol
|
symbol: this.data.symbol
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
|
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
|
||||||
import { UpdateBulkMarketDataDto } from '@ghostfolio/api/app/admin/update-bulk-market-data.dto';
|
|
||||||
import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto';
|
import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto';
|
||||||
import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto';
|
import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto';
|
||||||
import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto';
|
import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto';
|
||||||
@ -17,7 +16,6 @@ import {
|
|||||||
AdminData,
|
AdminData,
|
||||||
AdminJobs,
|
AdminJobs,
|
||||||
AdminMarketData,
|
AdminMarketData,
|
||||||
AdminMarketDataDetails,
|
|
||||||
AdminUsers,
|
AdminUsers,
|
||||||
DataProviderGhostfolioStatusResponse,
|
DataProviderGhostfolioStatusResponse,
|
||||||
EnhancedSymbolProfile,
|
EnhancedSymbolProfile,
|
||||||
@ -29,8 +27,8 @@ import { Injectable } from '@angular/core';
|
|||||||
import { SortDirection } from '@angular/material/sort';
|
import { SortDirection } from '@angular/material/sort';
|
||||||
import { DataSource, MarketData, Platform, Tag } from '@prisma/client';
|
import { DataSource, MarketData, Platform, Tag } from '@prisma/client';
|
||||||
import { JobStatus } from 'bull';
|
import { JobStatus } from 'bull';
|
||||||
import { format, parseISO } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import { Observable, map, switchMap } from 'rxjs';
|
import { switchMap } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
@ -125,25 +123,6 @@ export class AdminService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchAdminMarketDataBySymbol({
|
|
||||||
dataSource,
|
|
||||||
symbol
|
|
||||||
}: {
|
|
||||||
dataSource: DataSource;
|
|
||||||
symbol: string;
|
|
||||||
}): Observable<AdminMarketDataDetails> {
|
|
||||||
return this.http
|
|
||||||
.get<any>(`/api/v1/admin/market-data/${dataSource}/${symbol}`)
|
|
||||||
.pipe(
|
|
||||||
map((data) => {
|
|
||||||
for (const item of data.marketData) {
|
|
||||||
item.date = parseISO(item.date);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public fetchGhostfolioDataProviderStatus() {
|
public fetchGhostfolioDataProviderStatus() {
|
||||||
return this.fetchAdminData().pipe(
|
return this.fetchAdminData().pipe(
|
||||||
switchMap(({ settings }) => {
|
switchMap(({ settings }) => {
|
||||||
@ -278,20 +257,6 @@ export class AdminService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public postMarketData({
|
|
||||||
dataSource,
|
|
||||||
marketData,
|
|
||||||
symbol
|
|
||||||
}: {
|
|
||||||
dataSource: DataSource;
|
|
||||||
marketData: UpdateBulkMarketDataDto;
|
|
||||||
symbol: string;
|
|
||||||
}) {
|
|
||||||
const url = `/api/v1/admin/market-data/${dataSource}/${symbol}`;
|
|
||||||
|
|
||||||
return this.http.post<MarketData>(url, marketData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public postPlatform(aPlatform: CreatePlatformDto) {
|
public postPlatform(aPlatform: CreatePlatformDto) {
|
||||||
return this.http.post<Platform>(`/api/v1/platform`, aPlatform);
|
return this.http.post<Platform>(`/api/v1/platform`, aPlatform);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { CreateAccountBalanceDto } from '@ghostfolio/api/app/account-balance/cre
|
|||||||
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
|
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
|
||||||
import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
|
import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
|
||||||
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
||||||
|
import { UpdateBulkMarketDataDto } from '@ghostfolio/api/app/admin/update-bulk-market-data.dto';
|
||||||
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
||||||
import {
|
import {
|
||||||
Activities,
|
Activities,
|
||||||
@ -21,7 +22,6 @@ import {
|
|||||||
Access,
|
Access,
|
||||||
AccountBalancesResponse,
|
AccountBalancesResponse,
|
||||||
Accounts,
|
Accounts,
|
||||||
AdminMarketDataDetails,
|
|
||||||
ApiKeyResponse,
|
ApiKeyResponse,
|
||||||
AssetProfileIdentifier,
|
AssetProfileIdentifier,
|
||||||
BenchmarkMarketDataDetails,
|
BenchmarkMarketDataDetails,
|
||||||
@ -31,6 +31,7 @@ import {
|
|||||||
ImportResponse,
|
ImportResponse,
|
||||||
InfoItem,
|
InfoItem,
|
||||||
LookupResponse,
|
LookupResponse,
|
||||||
|
MarketDataDetailsResponse,
|
||||||
OAuthResponse,
|
OAuthResponse,
|
||||||
PortfolioDetails,
|
PortfolioDetails,
|
||||||
PortfolioDividends,
|
PortfolioDividends,
|
||||||
@ -51,6 +52,7 @@ import { SortDirection } from '@angular/material/sort';
|
|||||||
import {
|
import {
|
||||||
AccountBalance,
|
AccountBalance,
|
||||||
DataSource,
|
DataSource,
|
||||||
|
MarketData,
|
||||||
Order as OrderModel,
|
Order as OrderModel,
|
||||||
Tag
|
Tag
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
@ -316,7 +318,7 @@ export class DataService {
|
|||||||
public fetchAsset({
|
public fetchAsset({
|
||||||
dataSource,
|
dataSource,
|
||||||
symbol
|
symbol
|
||||||
}: AssetProfileIdentifier): Observable<AdminMarketDataDetails> {
|
}: AssetProfileIdentifier): Observable<MarketDataDetailsResponse> {
|
||||||
return this.http.get<any>(`/api/v1/asset/${dataSource}/${symbol}`).pipe(
|
return this.http.get<any>(`/api/v1/asset/${dataSource}/${symbol}`).pipe(
|
||||||
map((data) => {
|
map((data) => {
|
||||||
for (const item of data.marketData) {
|
for (const item of data.marketData) {
|
||||||
@ -431,6 +433,25 @@ export class DataService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fetchMarketDataBySymbol({
|
||||||
|
dataSource,
|
||||||
|
symbol
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
symbol: string;
|
||||||
|
}): Observable<MarketDataDetailsResponse> {
|
||||||
|
return this.http
|
||||||
|
.get<any>(`/api/v1/market-data/${dataSource}/${symbol}`)
|
||||||
|
.pipe(
|
||||||
|
map((data) => {
|
||||||
|
for (const item of data.marketData) {
|
||||||
|
item.date = parseISO(item.date);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public fetchSymbolItem({
|
public fetchSymbolItem({
|
||||||
dataSource,
|
dataSource,
|
||||||
includeHistoricalData,
|
includeHistoricalData,
|
||||||
@ -665,6 +686,20 @@ export class DataService {
|
|||||||
return this.http.post('/api/v1/benchmark', benchmark);
|
return this.http.post('/api/v1/benchmark', benchmark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public postMarketData({
|
||||||
|
dataSource,
|
||||||
|
marketData,
|
||||||
|
symbol
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
marketData: UpdateBulkMarketDataDto;
|
||||||
|
symbol: string;
|
||||||
|
}) {
|
||||||
|
const url = `/api/v1/market-data/${dataSource}/${symbol}`;
|
||||||
|
|
||||||
|
return this.http.post<MarketData>(url, marketData);
|
||||||
|
}
|
||||||
|
|
||||||
public postOrder(aOrder: CreateOrderDto) {
|
public postOrder(aOrder: CreateOrderDto) {
|
||||||
return this.http.post<OrderModel>('/api/v1/order', aOrder);
|
return this.http.post<OrderModel>('/api/v1/order', aOrder);
|
||||||
}
|
}
|
||||||
|
@ -30,4 +30,5 @@ export interface EnhancedSymbolProfile {
|
|||||||
symbolMapping?: { [key: string]: string };
|
symbolMapping?: { [key: string]: string };
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
url?: string;
|
url?: string;
|
||||||
|
userId?: string;
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ import type { ResponseError } from './responses/errors.interface';
|
|||||||
import type { HistoricalResponse } from './responses/historical-response.interface';
|
import type { HistoricalResponse } from './responses/historical-response.interface';
|
||||||
import type { ImportResponse } from './responses/import-response.interface';
|
import type { ImportResponse } from './responses/import-response.interface';
|
||||||
import type { LookupResponse } from './responses/lookup-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 type { OAuthResponse } from './responses/oauth-response.interface';
|
||||||
import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface';
|
import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface';
|
||||||
import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface';
|
import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface';
|
||||||
@ -97,6 +98,7 @@ export {
|
|||||||
LineChartItem,
|
LineChartItem,
|
||||||
LookupItem,
|
LookupItem,
|
||||||
LookupResponse,
|
LookupResponse,
|
||||||
|
MarketDataDetailsResponse,
|
||||||
OAuthResponse,
|
OAuthResponse,
|
||||||
PortfolioChart,
|
PortfolioChart,
|
||||||
PortfolioDetails,
|
PortfolioDetails,
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
import { MarketData } from '@prisma/client';
|
||||||
|
|
||||||
|
import { EnhancedSymbolProfile } from '../enhanced-symbol-profile.interface';
|
||||||
|
|
||||||
|
export interface MarketDataDetailsResponse {
|
||||||
|
assetProfile: Partial<EnhancedSymbolProfile>;
|
||||||
|
marketData: MarketData[];
|
||||||
|
}
|
@ -10,6 +10,8 @@ export const permissions = {
|
|||||||
createAccount: 'createAccount',
|
createAccount: 'createAccount',
|
||||||
createAccountBalance: 'createAccountBalance',
|
createAccountBalance: 'createAccountBalance',
|
||||||
createApiKey: 'createApiKey',
|
createApiKey: 'createApiKey',
|
||||||
|
createMarketData: 'createMarketData',
|
||||||
|
createMarketDataOfOwnAssetProfile: 'createMarketDataOfOwnAssetProfile',
|
||||||
createOrder: 'createOrder',
|
createOrder: 'createOrder',
|
||||||
createPlatform: 'createPlatform',
|
createPlatform: 'createPlatform',
|
||||||
createTag: 'createTag',
|
createTag: 'createTag',
|
||||||
@ -33,12 +35,16 @@ export const permissions = {
|
|||||||
enableSubscriptionInterstitial: 'enableSubscriptionInterstitial',
|
enableSubscriptionInterstitial: 'enableSubscriptionInterstitial',
|
||||||
enableSystemMessage: 'enableSystemMessage',
|
enableSystemMessage: 'enableSystemMessage',
|
||||||
impersonateAllUsers: 'impersonateAllUsers',
|
impersonateAllUsers: 'impersonateAllUsers',
|
||||||
|
readMarketData: 'readMarketData',
|
||||||
|
readMarketDataOfOwnAssetProfile: 'readMarketDataOfOwnAssetProfile',
|
||||||
readPlatforms: 'readPlatforms',
|
readPlatforms: 'readPlatforms',
|
||||||
readTags: 'readTags',
|
readTags: 'readTags',
|
||||||
reportDataGlitch: 'reportDataGlitch',
|
reportDataGlitch: 'reportDataGlitch',
|
||||||
toggleReadOnlyMode: 'toggleReadOnlyMode',
|
toggleReadOnlyMode: 'toggleReadOnlyMode',
|
||||||
updateAccount: 'updateAccount',
|
updateAccount: 'updateAccount',
|
||||||
updateAuthDevice: 'updateAuthDevice',
|
updateAuthDevice: 'updateAuthDevice',
|
||||||
|
updateMarketData: 'updateMarketData',
|
||||||
|
updateMarketDataOfOwnAssetProfile: 'updateMarketDataOfOwnAssetProfile',
|
||||||
updateOrder: 'updateOrder',
|
updateOrder: 'updateOrder',
|
||||||
updatePlatform: 'updatePlatform',
|
updatePlatform: 'updatePlatform',
|
||||||
updateTag: 'updateTag',
|
updateTag: 'updateTag',
|
||||||
@ -57,6 +63,8 @@ export function getPermissions(aRole: Role): string[] {
|
|||||||
permissions.createAccount,
|
permissions.createAccount,
|
||||||
permissions.createAccountBalance,
|
permissions.createAccountBalance,
|
||||||
permissions.deleteAccountBalance,
|
permissions.deleteAccountBalance,
|
||||||
|
permissions.createMarketData,
|
||||||
|
permissions.createMarketDataOfOwnAssetProfile,
|
||||||
permissions.createOrder,
|
permissions.createOrder,
|
||||||
permissions.createPlatform,
|
permissions.createPlatform,
|
||||||
permissions.createTag,
|
permissions.createTag,
|
||||||
@ -68,10 +76,14 @@ export function getPermissions(aRole: Role): string[] {
|
|||||||
permissions.deletePlatform,
|
permissions.deletePlatform,
|
||||||
permissions.deleteTag,
|
permissions.deleteTag,
|
||||||
permissions.deleteUser,
|
permissions.deleteUser,
|
||||||
|
permissions.readMarketData,
|
||||||
|
permissions.readMarketDataOfOwnAssetProfile,
|
||||||
permissions.readPlatforms,
|
permissions.readPlatforms,
|
||||||
permissions.readTags,
|
permissions.readTags,
|
||||||
permissions.updateAccount,
|
permissions.updateAccount,
|
||||||
permissions.updateAuthDevice,
|
permissions.updateAuthDevice,
|
||||||
|
permissions.updateMarketData,
|
||||||
|
permissions.updateMarketDataOfOwnAssetProfile,
|
||||||
permissions.updateOrder,
|
permissions.updateOrder,
|
||||||
permissions.updatePlatform,
|
permissions.updatePlatform,
|
||||||
permissions.updateTag,
|
permissions.updateTag,
|
||||||
@ -93,6 +105,7 @@ export function getPermissions(aRole: Role): string[] {
|
|||||||
permissions.createAccess,
|
permissions.createAccess,
|
||||||
permissions.createAccount,
|
permissions.createAccount,
|
||||||
permissions.createAccountBalance,
|
permissions.createAccountBalance,
|
||||||
|
permissions.createMarketDataOfOwnAssetProfile,
|
||||||
permissions.createOrder,
|
permissions.createOrder,
|
||||||
permissions.deleteAccess,
|
permissions.deleteAccess,
|
||||||
permissions.deleteAccount,
|
permissions.deleteAccount,
|
||||||
@ -100,8 +113,10 @@ export function getPermissions(aRole: Role): string[] {
|
|||||||
permissions.deleteAuthDevice,
|
permissions.deleteAuthDevice,
|
||||||
permissions.deleteOrder,
|
permissions.deleteOrder,
|
||||||
permissions.deleteOwnUser,
|
permissions.deleteOwnUser,
|
||||||
|
permissions.readMarketDataOfOwnAssetProfile,
|
||||||
permissions.updateAccount,
|
permissions.updateAccount,
|
||||||
permissions.updateAuthDevice,
|
permissions.updateAuthDevice,
|
||||||
|
permissions.updateMarketDataOfOwnAssetProfile,
|
||||||
permissions.updateOrder,
|
permissions.updateOrder,
|
||||||
permissions.updateUserSettings,
|
permissions.updateUserSettings,
|
||||||
permissions.updateViewMode
|
permissions.updateViewMode
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||||
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
@ -51,6 +52,7 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy {
|
|||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
@Inject(MAT_DIALOG_DATA)
|
@Inject(MAT_DIALOG_DATA)
|
||||||
public data: HistoricalMarketDataEditorDialogParams,
|
public data: HistoricalMarketDataEditorDialogParams,
|
||||||
|
private dataService: DataService,
|
||||||
private dateAdapter: DateAdapter<any>,
|
private dateAdapter: DateAdapter<any>,
|
||||||
public dialogRef: MatDialogRef<GfHistoricalMarketDataEditorDialogComponent>,
|
public dialogRef: MatDialogRef<GfHistoricalMarketDataEditorDialogComponent>,
|
||||||
@Inject(MAT_DATE_LOCALE) private locale: string
|
@Inject(MAT_DATE_LOCALE) private locale: string
|
||||||
@ -81,7 +83,7 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onUpdate() {
|
public onUpdate() {
|
||||||
this.adminService
|
this.dataService
|
||||||
.postMarketData({
|
.postMarketData({
|
||||||
dataSource: this.data.dataSource,
|
dataSource: this.data.dataSource,
|
||||||
marketData: {
|
marketData: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto';
|
import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto';
|
||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import {
|
import {
|
||||||
DATE_FORMAT,
|
DATE_FORMAT,
|
||||||
getDateFormatString,
|
getDateFormatString,
|
||||||
@ -90,7 +90,7 @@ export class GfHistoricalMarketDataEditorComponent
|
|||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private adminService: AdminService,
|
private dataService: DataService,
|
||||||
private deviceService: DeviceDetectorService,
|
private deviceService: DeviceDetectorService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
@ -236,7 +236,7 @@ export class GfHistoricalMarketDataEditorComponent
|
|||||||
}
|
}
|
||||||
).data as UpdateMarketDataDto[];
|
).data as UpdateMarketDataDto[];
|
||||||
|
|
||||||
this.adminService
|
this.dataService
|
||||||
.postMarketData({
|
.postMarketData({
|
||||||
dataSource: this.dataSource,
|
dataSource: this.dataSource,
|
||||||
marketData: {
|
marketData: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user