import type { Request } from 'express'; import { BadRequestException, Body, Controller, Get, HttpCode, HttpStatus, Param, ParseUUIDPipe, Post, Query, Req, UseGuards, ValidationPipe, } from '@nestjs/common'; import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiProblemResponses } from '../../../../../common/decorators/api-safe-error-response.decorator'; import { AccessPayload, JwtAuthGuard } from '../../../../auth/auth.api'; import { InvestmentReadQueryDto } from '../../../investment-resource.dto'; import { GetStockCompanyUseCase } from '../application/get-stock-company.use-case'; import { StockCatalogService } from '../application/stock-catalog.service'; import { StockIncomeService } from '../application/stock-income.service'; import { StockMarketService } from '../application/stock-market.service'; import { StockScreenerService } from '../application/stock-screener.service'; import { StockCandlesQueryDto, StockCandlesResourceDto, StockCompanyResourceDto, StockDividendsResourceDto, StockPositionResourceDto, StockQuoteResourceDto, StockResourceDto, StockScreenerCriteriaDto, StockScreenerPageDto, StockScreenerPageQueryDto, StockSearchQueryDto, StockSearchResourceDto, StockTickerParamsDto, } from './stock.dto'; @ApiTags('stocks') @ApiProblemResponses() @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Controller('stocks') export class StockController { constructor( private readonly stocks: StockCatalogService, private readonly market: StockMarketService, private readonly income: StockIncomeService, private readonly companyInfo: GetStockCompanyUseCase, private readonly screener: StockScreenerService, ) {} @Get() @ApiOkResponse({ type: StockSearchResourceDto }) search( @Req() request: Request & { user: AccessPayload }, @Query() query: StockSearchQueryDto, ) { return this.stocks.search( request.user.sub, query.query, query.sync === 'observe', ); } @Get('by-ticker/:ticker') @ApiOkResponse({ type: StockResourceDto }) getByTicker( @Req() request: Request & { user: AccessPayload }, @Param() params: StockTickerParamsDto, @Query() query: InvestmentReadQueryDto, ) { return this.stocks.getByTicker( request.user.sub, params.ticker, query.sync === 'observe', ); } @Post('screener/search') @HttpCode(HttpStatus.OK) @ApiOperation({ operationId: 'createStockScreenerSearch' }) @ApiOkResponse({ type: StockScreenerPageDto }) searchScreener( @Req() request: Request & { user: AccessPayload }, @Body(new ValidationPipe({ forbidNonWhitelisted: true, forbidUnknownValues: true, whitelist: true })) criteria: StockScreenerCriteriaDto, ) { const allowed = new Set(['countryOfRisk', 'currency', 'maxLot', 'query', 'realExchange', 'sector', 'shareType']); if (Object.keys(request.body as object).some((key) => !allowed.has(key))) { throw new BadRequestException(); } return this.screener.search(request.user.sub, criteria); } @Get('screener/search/:searchId') @ApiOperation({ operationId: 'readStockScreenerPage' }) @ApiOkResponse({ type: StockScreenerPageDto }) pageScreener( @Req() request: Request & { user: AccessPayload }, @Param('searchId') searchId: string, @Query() query: StockScreenerPageQueryDto, ) { return this.screener.page(request.user.sub, searchId, query.page); } @Get(':uid') @ApiOkResponse({ type: StockResourceDto }) get( @Req() request: Request & { user: AccessPayload }, @Param('uid', new ParseUUIDPipe()) uid: string, @Query() query: InvestmentReadQueryDto, ) { return this.stocks.get(request.user.sub, uid, query.sync === 'observe'); } @Get(':uid/quote') @ApiOkResponse({ type: StockQuoteResourceDto }) quote( @Req() request: Request & { user: AccessPayload }, @Param('uid', new ParseUUIDPipe()) uid: string, @Query() query: InvestmentReadQueryDto, ) { return this.market.quote(request.user.sub, uid, query.sync === 'observe'); } @Get(':uid/candles') @ApiOkResponse({ type: StockCandlesResourceDto }) candles( @Req() request: Request & { user: AccessPayload }, @Param('uid', new ParseUUIDPipe()) uid: string, @Query() query: StockCandlesQueryDto, ) { return this.market.candles( request.user.sub, uid, query.period, query.sync === 'observe', ); } @Get(':uid/position') @ApiOkResponse({ type: StockPositionResourceDto }) position( @Req() request: Request & { user: AccessPayload }, @Param('uid', new ParseUUIDPipe()) uid: string, ) { return this.income.readPosition(request.user.sub, uid); } @Get(':uid/company') @ApiOkResponse({ type: StockCompanyResourceDto }) company( @Req() request: Request & { user: AccessPayload }, @Param('uid', new ParseUUIDPipe()) uid: string, @Query() query: InvestmentReadQueryDto, ) { return this.companyInfo.execute( request.user.sub, uid, query.sync === 'observe', ); } @Get(':uid/dividends') @ApiOkResponse({ type: StockDividendsResourceDto }) dividends( @Req() request: Request & { user: AccessPayload }, @Param('uid', new ParseUUIDPipe()) uid: string, @Query() query: InvestmentReadQueryDto, ) { return this.income.dividends( request.user.sub, uid, query.sync === 'observe', ); } }