mirror of https://github.com/ghostfolio/ghostfolio
Browse Source
* Extend public API with endpoint to update asset profile data * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>pull/7016/head
committed by
GitHub
11 changed files with 262 additions and 12 deletions
@ -0,0 +1,51 @@ |
|||||
|
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; |
||||
|
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; |
||||
|
import { UpdateAssetProfileDataDto } from '@ghostfolio/common/dtos'; |
||||
|
import { EnhancedSymbolProfile } from '@ghostfolio/common/interfaces'; |
||||
|
import { permissions } from '@ghostfolio/common/permissions'; |
||||
|
import { RequestWithUser } from '@ghostfolio/common/types'; |
||||
|
|
||||
|
import { |
||||
|
Body, |
||||
|
Controller, |
||||
|
HttpException, |
||||
|
Inject, |
||||
|
Param, |
||||
|
Patch, |
||||
|
UseGuards |
||||
|
} from '@nestjs/common'; |
||||
|
import { REQUEST } from '@nestjs/core'; |
||||
|
import { AuthGuard } from '@nestjs/passport'; |
||||
|
import { DataSource } from '@prisma/client'; |
||||
|
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; |
||||
|
|
||||
|
import { AssetProfilesService } from './asset-profiles.service'; |
||||
|
|
||||
|
@Controller('asset-profiles') |
||||
|
export class AssetProfilesController { |
||||
|
public constructor( |
||||
|
private readonly assetProfilesService: AssetProfilesService, |
||||
|
@Inject(REQUEST) private readonly request: RequestWithUser |
||||
|
) {} |
||||
|
|
||||
|
@HasPermission(permissions.accessAdminControl) |
||||
|
@Patch(':dataSource/:symbol') |
||||
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) |
||||
|
public async updateAssetProfileData( |
||||
|
@Body() assetProfileData: UpdateAssetProfileDataDto, |
||||
|
@Param('dataSource') dataSource: DataSource, |
||||
|
@Param('symbol') symbol: string |
||||
|
): Promise<EnhancedSymbolProfile> { |
||||
|
if (!this.request.user.settings.settings.isExperimentalFeatures) { |
||||
|
throw new HttpException( |
||||
|
getReasonPhrase(StatusCodes.NOT_FOUND), |
||||
|
StatusCodes.NOT_FOUND |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return this.assetProfilesService.updateAssetProfileData( |
||||
|
{ dataSource, symbol }, |
||||
|
assetProfileData |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; |
||||
|
|
||||
|
import { Module } from '@nestjs/common'; |
||||
|
|
||||
|
import { AssetProfilesController } from './asset-profiles.controller'; |
||||
|
import { AssetProfilesService } from './asset-profiles.service'; |
||||
|
|
||||
|
@Module({ |
||||
|
controllers: [AssetProfilesController], |
||||
|
imports: [SymbolProfileModule], |
||||
|
providers: [AssetProfilesService] |
||||
|
}) |
||||
|
export class AssetProfilesModule {} |
||||
@ -0,0 +1,90 @@ |
|||||
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; |
||||
|
import { UpdateAssetProfileDataDto } from '@ghostfolio/common/dtos'; |
||||
|
import { |
||||
|
AssetProfileIdentifier, |
||||
|
EnhancedSymbolProfile |
||||
|
} from '@ghostfolio/common/interfaces'; |
||||
|
|
||||
|
import { Injectable, NotFoundException } from '@nestjs/common'; |
||||
|
import { Prisma } from '@prisma/client'; |
||||
|
|
||||
|
@Injectable() |
||||
|
export class AssetProfilesService { |
||||
|
public constructor( |
||||
|
private readonly symbolProfileService: SymbolProfileService |
||||
|
) {} |
||||
|
|
||||
|
public async updateAssetProfileData( |
||||
|
{ dataSource, symbol }: AssetProfileIdentifier, |
||||
|
assetProfileData: UpdateAssetProfileDataDto |
||||
|
): Promise<EnhancedSymbolProfile> { |
||||
|
const notFoundMessage = `Could not find the asset profile for ${symbol} (${dataSource})`; |
||||
|
|
||||
|
const data = this.getAssetProfileDataUpdate(assetProfileData); |
||||
|
|
||||
|
if (Object.keys(data).length > 0) { |
||||
|
try { |
||||
|
await this.symbolProfileService.updateSymbolProfile( |
||||
|
{ |
||||
|
dataSource, |
||||
|
symbol |
||||
|
}, |
||||
|
this.symbolProfileService.getAssetProfileUpdateInput( |
||||
|
{ dataSource, symbol }, |
||||
|
data |
||||
|
) |
||||
|
); |
||||
|
} catch (error) { |
||||
|
if ( |
||||
|
error instanceof Prisma.PrismaClientKnownRequestError && |
||||
|
error.code === 'P2025' |
||||
|
) { |
||||
|
throw new NotFoundException(notFoundMessage); |
||||
|
} |
||||
|
|
||||
|
throw error; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ |
||||
|
{ |
||||
|
dataSource, |
||||
|
symbol |
||||
|
} |
||||
|
]); |
||||
|
|
||||
|
if (!assetProfile) { |
||||
|
throw new NotFoundException(notFoundMessage); |
||||
|
} |
||||
|
|
||||
|
return assetProfile; |
||||
|
} |
||||
|
|
||||
|
private getAssetProfileDataUpdate({ |
||||
|
countries, |
||||
|
holdings, |
||||
|
sectors |
||||
|
}: UpdateAssetProfileDataDto): Pick< |
||||
|
Prisma.SymbolProfileUpdateInput, |
||||
|
'countries' | 'holdings' | 'sectors' |
||||
|
> { |
||||
|
const data: Pick< |
||||
|
Prisma.SymbolProfileUpdateInput, |
||||
|
'countries' | 'holdings' | 'sectors' |
||||
|
> = {}; |
||||
|
|
||||
|
if (countries !== undefined) { |
||||
|
data.countries = countries as Prisma.JsonArray; |
||||
|
} |
||||
|
|
||||
|
if (holdings !== undefined) { |
||||
|
data.holdings = holdings as Prisma.JsonArray; |
||||
|
} |
||||
|
|
||||
|
if (sectors !== undefined) { |
||||
|
data.sectors = sectors as Prisma.JsonArray; |
||||
|
} |
||||
|
|
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import { Prisma } from '@prisma/client'; |
||||
|
import { IsArray, IsOptional } from 'class-validator'; |
||||
|
|
||||
|
export class UpdateAssetProfileDataDto { |
||||
|
@IsArray() |
||||
|
@IsOptional() |
||||
|
countries?: Prisma.InputJsonArray; |
||||
|
|
||||
|
@IsArray() |
||||
|
@IsOptional() |
||||
|
holdings?: Prisma.InputJsonArray; |
||||
|
|
||||
|
@IsArray() |
||||
|
@IsOptional() |
||||
|
sectors?: Prisma.InputJsonArray; |
||||
|
} |
||||
Loading…
Reference in new issue