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/),
|
||||
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
|
||||
|
||||
### Added
|
||||
|
@ -214,6 +214,9 @@ export class AdminController {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Get('market-data/:dataSource/:symbol')
|
||||
@HasPermission(permissions.accessAdminControl)
|
||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||
@ -250,6 +253,9 @@ export class AdminController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@HasPermission(permissions.accessAdminControl)
|
||||
@Post('market-data/:dataSource/:symbol')
|
||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||
|
@ -33,6 +33,7 @@ import { BenchmarkModule } from './benchmark/benchmark.module';
|
||||
import { CacheModule } from './cache/cache.module';
|
||||
import { ApiKeysModule } from './endpoints/api-keys/api-keys.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 { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
|
||||
import { ExportModule } from './export/export.module';
|
||||
@ -84,6 +85,7 @@ import { UserModule } from './user/user.module';
|
||||
ImportModule,
|
||||
InfoModule,
|
||||
LogoModule,
|
||||
MarketDataModule,
|
||||
OrderModule,
|
||||
PlatformModule,
|
||||
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
|
||||
.fetchAdminMarketDataBySymbol({
|
||||
this.dataService
|
||||
.fetchMarketDataBySymbol({
|
||||
dataSource: this.data.dataSource,
|
||||
symbol: this.data.symbol
|
||||
})
|
||||
|
@ -1,5 +1,4 @@
|
||||
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 { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto';
|
||||
import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto';
|
||||
@ -17,7 +16,6 @@ import {
|
||||
AdminData,
|
||||
AdminJobs,
|
||||
AdminMarketData,
|
||||
AdminMarketDataDetails,
|
||||
AdminUsers,
|
||||
DataProviderGhostfolioStatusResponse,
|
||||
EnhancedSymbolProfile,
|
||||
@ -29,8 +27,8 @@ import { Injectable } from '@angular/core';
|
||||
import { SortDirection } from '@angular/material/sort';
|
||||
import { DataSource, MarketData, Platform, Tag } from '@prisma/client';
|
||||
import { JobStatus } from 'bull';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
import { Observable, map, switchMap } from 'rxjs';
|
||||
import { format } from 'date-fns';
|
||||
import { switchMap } from 'rxjs';
|
||||
|
||||
import { environment } from '../../environments/environment';
|
||||
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() {
|
||||
return this.fetchAdminData().pipe(
|
||||
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) {
|
||||
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 { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.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 {
|
||||
Activities,
|
||||
@ -21,7 +22,6 @@ import {
|
||||
Access,
|
||||
AccountBalancesResponse,
|
||||
Accounts,
|
||||
AdminMarketDataDetails,
|
||||
ApiKeyResponse,
|
||||
AssetProfileIdentifier,
|
||||
BenchmarkMarketDataDetails,
|
||||
@ -31,6 +31,7 @@ import {
|
||||
ImportResponse,
|
||||
InfoItem,
|
||||
LookupResponse,
|
||||
MarketDataDetailsResponse,
|
||||
OAuthResponse,
|
||||
PortfolioDetails,
|
||||
PortfolioDividends,
|
||||
@ -51,6 +52,7 @@ import { SortDirection } from '@angular/material/sort';
|
||||
import {
|
||||
AccountBalance,
|
||||
DataSource,
|
||||
MarketData,
|
||||
Order as OrderModel,
|
||||
Tag
|
||||
} from '@prisma/client';
|
||||
@ -316,7 +318,7 @@ export class DataService {
|
||||
public fetchAsset({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier): Observable<AdminMarketDataDetails> {
|
||||
}: AssetProfileIdentifier): Observable<MarketDataDetailsResponse> {
|
||||
return this.http.get<any>(`/api/v1/asset/${dataSource}/${symbol}`).pipe(
|
||||
map((data) => {
|
||||
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({
|
||||
dataSource,
|
||||
includeHistoricalData,
|
||||
@ -665,6 +686,20 @@ export class DataService {
|
||||
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) {
|
||||
return this.http.post<OrderModel>('/api/v1/order', aOrder);
|
||||
}
|
||||
|
@ -30,4 +30,5 @@ export interface EnhancedSymbolProfile {
|
||||
symbolMapping?: { [key: string]: string };
|
||||
updatedAt: Date;
|
||||
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 { 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 type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface';
|
||||
import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface';
|
||||
@ -97,6 +98,7 @@ export {
|
||||
LineChartItem,
|
||||
LookupItem,
|
||||
LookupResponse,
|
||||
MarketDataDetailsResponse,
|
||||
OAuthResponse,
|
||||
PortfolioChart,
|
||||
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',
|
||||
createAccountBalance: 'createAccountBalance',
|
||||
createApiKey: 'createApiKey',
|
||||
createMarketData: 'createMarketData',
|
||||
createMarketDataOfOwnAssetProfile: 'createMarketDataOfOwnAssetProfile',
|
||||
createOrder: 'createOrder',
|
||||
createPlatform: 'createPlatform',
|
||||
createTag: 'createTag',
|
||||
@ -33,12 +35,16 @@ export const permissions = {
|
||||
enableSubscriptionInterstitial: 'enableSubscriptionInterstitial',
|
||||
enableSystemMessage: 'enableSystemMessage',
|
||||
impersonateAllUsers: 'impersonateAllUsers',
|
||||
readMarketData: 'readMarketData',
|
||||
readMarketDataOfOwnAssetProfile: 'readMarketDataOfOwnAssetProfile',
|
||||
readPlatforms: 'readPlatforms',
|
||||
readTags: 'readTags',
|
||||
reportDataGlitch: 'reportDataGlitch',
|
||||
toggleReadOnlyMode: 'toggleReadOnlyMode',
|
||||
updateAccount: 'updateAccount',
|
||||
updateAuthDevice: 'updateAuthDevice',
|
||||
updateMarketData: 'updateMarketData',
|
||||
updateMarketDataOfOwnAssetProfile: 'updateMarketDataOfOwnAssetProfile',
|
||||
updateOrder: 'updateOrder',
|
||||
updatePlatform: 'updatePlatform',
|
||||
updateTag: 'updateTag',
|
||||
@ -57,6 +63,8 @@ export function getPermissions(aRole: Role): string[] {
|
||||
permissions.createAccount,
|
||||
permissions.createAccountBalance,
|
||||
permissions.deleteAccountBalance,
|
||||
permissions.createMarketData,
|
||||
permissions.createMarketDataOfOwnAssetProfile,
|
||||
permissions.createOrder,
|
||||
permissions.createPlatform,
|
||||
permissions.createTag,
|
||||
@ -68,10 +76,14 @@ export function getPermissions(aRole: Role): string[] {
|
||||
permissions.deletePlatform,
|
||||
permissions.deleteTag,
|
||||
permissions.deleteUser,
|
||||
permissions.readMarketData,
|
||||
permissions.readMarketDataOfOwnAssetProfile,
|
||||
permissions.readPlatforms,
|
||||
permissions.readTags,
|
||||
permissions.updateAccount,
|
||||
permissions.updateAuthDevice,
|
||||
permissions.updateMarketData,
|
||||
permissions.updateMarketDataOfOwnAssetProfile,
|
||||
permissions.updateOrder,
|
||||
permissions.updatePlatform,
|
||||
permissions.updateTag,
|
||||
@ -93,6 +105,7 @@ export function getPermissions(aRole: Role): string[] {
|
||||
permissions.createAccess,
|
||||
permissions.createAccount,
|
||||
permissions.createAccountBalance,
|
||||
permissions.createMarketDataOfOwnAssetProfile,
|
||||
permissions.createOrder,
|
||||
permissions.deleteAccess,
|
||||
permissions.deleteAccount,
|
||||
@ -100,8 +113,10 @@ export function getPermissions(aRole: Role): string[] {
|
||||
permissions.deleteAuthDevice,
|
||||
permissions.deleteOrder,
|
||||
permissions.deleteOwnUser,
|
||||
permissions.readMarketDataOfOwnAssetProfile,
|
||||
permissions.updateAccount,
|
||||
permissions.updateAuthDevice,
|
||||
permissions.updateMarketDataOfOwnAssetProfile,
|
||||
permissions.updateOrder,
|
||||
permissions.updateUserSettings,
|
||||
permissions.updateViewMode
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
@ -51,6 +52,7 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy {
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public data: HistoricalMarketDataEditorDialogParams,
|
||||
private dataService: DataService,
|
||||
private dateAdapter: DateAdapter<any>,
|
||||
public dialogRef: MatDialogRef<GfHistoricalMarketDataEditorDialogComponent>,
|
||||
@Inject(MAT_DATE_LOCALE) private locale: string
|
||||
@ -81,7 +83,7 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
public onUpdate() {
|
||||
this.adminService
|
||||
this.dataService
|
||||
.postMarketData({
|
||||
dataSource: this.data.dataSource,
|
||||
marketData: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
DATE_FORMAT,
|
||||
getDateFormatString,
|
||||
@ -90,7 +90,7 @@ export class GfHistoricalMarketDataEditorComponent
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
public constructor(
|
||||
private adminService: AdminService,
|
||||
private dataService: DataService,
|
||||
private deviceService: DeviceDetectorService,
|
||||
private dialog: MatDialog,
|
||||
private formBuilder: FormBuilder,
|
||||
@ -236,7 +236,7 @@ export class GfHistoricalMarketDataEditorComponent
|
||||
}
|
||||
).data as UpdateMarketDataDto[];
|
||||
|
||||
this.adminService
|
||||
this.dataService
|
||||
.postMarketData({
|
||||
dataSource: this.dataSource,
|
||||
marketData: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user