mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
7 changed files with 213 additions and 2 deletions
@ -0,0 +1,10 @@ |
|||
import { DataSource } from '@prisma/client'; |
|||
import { IsEnum, IsString } from 'class-validator'; |
|||
|
|||
export class CreateWatchlistItemDto { |
|||
@IsEnum(DataSource) |
|||
dataSource: DataSource; |
|||
|
|||
@IsString() |
|||
symbol: string; |
|||
} |
@ -0,0 +1,85 @@ |
|||
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; |
|||
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; |
|||
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; |
|||
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; |
|||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; |
|||
import { permissions } from '@ghostfolio/common/permissions'; |
|||
import { RequestWithUser } from '@ghostfolio/common/types'; |
|||
|
|||
import { |
|||
Body, |
|||
Controller, |
|||
Delete, |
|||
Get, |
|||
HttpException, |
|||
Inject, |
|||
Param, |
|||
Post, |
|||
UseGuards, |
|||
UseInterceptors |
|||
} 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 { CreateWatchlistItemDto } from './create-watchlist-item.dto'; |
|||
import { WatchlistService } from './watchlist.service'; |
|||
|
|||
@Controller('watchlist') |
|||
export class WatchlistController { |
|||
public constructor( |
|||
@Inject(REQUEST) private readonly request: RequestWithUser, |
|||
private readonly watchlistService: WatchlistService |
|||
) {} |
|||
|
|||
@Post() |
|||
@HasPermission(permissions.createWatchlistItem) |
|||
@UseGuards(AuthGuard('jwt')) |
|||
@UseInterceptors(TransformDataSourceInRequestInterceptor) |
|||
public async createWatchlistItem(@Body() data: CreateWatchlistItemDto) { |
|||
return this.watchlistService.createWatchlistItem({ |
|||
dataSource: data.dataSource, |
|||
symbol: data.symbol, |
|||
userId: this.request.user.id |
|||
}); |
|||
} |
|||
|
|||
@Delete(':dataSource/:symbol') |
|||
@HasPermission(permissions.deleteWatchlistItem) |
|||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) |
|||
@UseInterceptors(TransformDataSourceInRequestInterceptor) |
|||
public async deleteWatchlistItem( |
|||
@Param('dataSource') dataSource: DataSource, |
|||
@Param('symbol') symbol: string |
|||
) { |
|||
const watchlistItem = await this.watchlistService |
|||
.getWatchlistItems(this.request.user.id) |
|||
.then((items) => { |
|||
return items.find((item) => { |
|||
return item.dataSource === dataSource && item.symbol === symbol; |
|||
}); |
|||
}); |
|||
|
|||
if (!watchlistItem) { |
|||
throw new HttpException( |
|||
getReasonPhrase(StatusCodes.NOT_FOUND), |
|||
StatusCodes.NOT_FOUND |
|||
); |
|||
} |
|||
|
|||
return this.watchlistService.deleteWatchlistItem({ |
|||
dataSource, |
|||
symbol, |
|||
userId: this.request.user.id |
|||
}); |
|||
} |
|||
|
|||
@Get() |
|||
@HasPermission(permissions.readWatchlist) |
|||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) |
|||
@UseInterceptors(TransformDataSourceInResponseInterceptor) |
|||
public async getWatchlistItems(): Promise<AssetProfileIdentifier[]> { |
|||
return this.watchlistService.getWatchlistItems(this.request.user.id); |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; |
|||
import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; |
|||
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; |
|||
|
|||
import { Module } from '@nestjs/common'; |
|||
|
|||
import { WatchlistController } from './watchlist.controller'; |
|||
import { WatchlistService } from './watchlist.service'; |
|||
|
|||
@Module({ |
|||
controllers: [WatchlistController], |
|||
imports: [ |
|||
PrismaModule, |
|||
TransformDataSourceInRequestModule, |
|||
TransformDataSourceInResponseModule |
|||
], |
|||
providers: [WatchlistService] |
|||
}) |
|||
export class WatchlistModule {} |
@ -0,0 +1,79 @@ |
|||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; |
|||
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; |
|||
|
|||
import { Injectable, NotFoundException } from '@nestjs/common'; |
|||
import { DataSource } from '@prisma/client'; |
|||
|
|||
@Injectable() |
|||
export class WatchlistService { |
|||
public constructor(private readonly prismaService: PrismaService) {} |
|||
|
|||
public async createWatchlistItem({ |
|||
dataSource, |
|||
symbol, |
|||
userId |
|||
}: { |
|||
dataSource: DataSource; |
|||
symbol: string; |
|||
userId: string; |
|||
}): Promise<void> { |
|||
const symbolProfile = await this.prismaService.symbolProfile.findUnique({ |
|||
where: { |
|||
dataSource_symbol: { dataSource, symbol } |
|||
} |
|||
}); |
|||
|
|||
if (!symbolProfile) { |
|||
throw new NotFoundException( |
|||
`Asset profile not found for ${symbol} (${dataSource})` |
|||
); |
|||
} |
|||
|
|||
await this.prismaService.user.update({ |
|||
data: { |
|||
watchlist: { |
|||
connect: { |
|||
dataSource_symbol: { dataSource, symbol } |
|||
} |
|||
} |
|||
}, |
|||
where: { id: userId } |
|||
}); |
|||
} |
|||
|
|||
public async deleteWatchlistItem({ |
|||
dataSource, |
|||
symbol, |
|||
userId |
|||
}: { |
|||
dataSource: DataSource; |
|||
symbol: string; |
|||
userId: string; |
|||
}) { |
|||
await this.prismaService.user.update({ |
|||
data: { |
|||
watchlist: { |
|||
disconnect: { |
|||
dataSource_symbol: { dataSource, symbol } |
|||
} |
|||
} |
|||
}, |
|||
where: { id: userId } |
|||
}); |
|||
} |
|||
|
|||
public async getWatchlistItems( |
|||
userId: string |
|||
): Promise<AssetProfileIdentifier[]> { |
|||
const user = await this.prismaService.user.findUnique({ |
|||
select: { |
|||
watchlist: { |
|||
select: { dataSource: true, symbol: true } |
|||
} |
|||
}, |
|||
where: { id: userId } |
|||
}); |
|||
|
|||
return user.watchlist ?? []; |
|||
} |
|||
} |
Loading…
Reference in new issue