Browse Source

feat(api): add watchlist API endpoints

pull/4570/head
KenTandrian 4 months ago
committed by Thomas Kaul
parent
commit
13388d23af
  1. 4
      apps/api/src/app/app.module.ts
  2. 10
      apps/api/src/app/endpoints/watchlists/create-watchlist-item.dto.ts
  3. 78
      apps/api/src/app/endpoints/watchlists/watchlist.controller.ts
  4. 13
      apps/api/src/app/endpoints/watchlists/watchlist.module.ts
  5. 90
      apps/api/src/app/endpoints/watchlists/watchlist.service.ts

4
apps/api/src/app/app.module.ts

@ -38,6 +38,7 @@ import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfol
import { MarketDataModule } from './endpoints/market-data/market-data.module';
import { PublicModule } from './endpoints/public/public.module';
import { TagsModule } from './endpoints/tags/tags.module';
import { WatchlistModule } from './endpoints/watchlists/watchlist.module';
import { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
import { ExportModule } from './export/export.module';
import { HealthModule } from './health/health.module';
@ -128,7 +129,8 @@ import { UserModule } from './user/user.module';
SymbolModule,
TagsModule,
TwitterBotModule,
UserModule
UserModule,
WatchlistModule
],
providers: [CronService]
})

10
apps/api/src/app/endpoints/watchlists/create-watchlist-item.dto.ts

@ -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;
}

78
apps/api/src/app/endpoints/watchlists/watchlist.controller.ts

@ -0,0 +1,78 @@
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
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
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
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
) {}
@Get()
@HasPermission(permissions.readWatchlistItems)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getWatchlistItems(): Promise<AssetProfileIdentifier[]> {
return this.watchlistService.getWatchlistItems(this.request.user.id);
}
@Post()
@HasPermission(permissions.createWatchlistItem)
@UseGuards(AuthGuard('jwt'))
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)
public async deleteWatchlistItem(
@Param('dataSource') dataSource: string,
@Param('symbol') symbol: string
) {
const watchlistItem = await this.watchlistService
.getWatchlistItems(this.request.user.id)
.then((items) => {
return items.find(
(item) => item.dataSource === dataSource && item.symbol === symbol
);
});
if (!watchlistItem) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
return this.watchlistService.deleteWatchlistItem({
dataSource: watchlistItem.dataSource,
symbol,
userId: this.request.user.id
});
}
}

13
apps/api/src/app/endpoints/watchlists/watchlist.module.ts

@ -0,0 +1,13 @@
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],
providers: [WatchlistService]
})
export class WatchlistModule {}

90
apps/api/src/app/endpoints/watchlists/watchlist.service.ts

@ -0,0 +1,90 @@
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 getWatchlistItems(
userId: string
): Promise<AssetProfileIdentifier[]> {
const user = await this.prismaService.user.findUnique({
where: {
id: userId
},
select: {
watchlist: {
select: {
dataSource: true,
symbol: true
}
}
}
});
if (!user) {
throw new NotFoundException(
`User watchlist with ID ${userId} not found.`
);
}
return user.watchlist ?? [];
}
public async createWatchlistItem({
userId,
dataSource,
symbol
}: {
userId: string;
dataSource: DataSource;
symbol: string;
}): Promise<void> {
const symbolProfile = await this.prismaService.symbolProfile.findUnique({
where: { dataSource_symbol: { dataSource, symbol } }
});
if (!symbolProfile) {
throw new NotFoundException(`Symbol ${symbol} not found.`);
}
await this.prismaService.user.update({
where: { id: userId },
data: {
watchlist: {
connect: {
dataSource_symbol: {
dataSource,
symbol
}
}
}
}
});
}
public async deleteWatchlistItem({
userId,
dataSource,
symbol
}: {
userId: string;
dataSource: DataSource;
symbol: string;
}) {
await this.prismaService.user.update({
where: { id: userId },
data: {
watchlist: {
disconnect: {
dataSource_symbol: {
dataSource,
symbol
}
}
}
}
});
}
}
Loading…
Cancel
Save