Files
backend/src/modules/investments/instruments/stocks/presentation/stock.controller.ts
T
ksv741 7d10ef8adf
Backend verify / commits (pull_request) Successful in 28s
Backend verify / security (pull_request) Successful in 11s
Backend verify / verify (pull_request) Successful in 4m45s
feat: add stock screener
2026-10-11 11:18:21 +03:00

184 lines
5.3 KiB
TypeScript

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',
);
}
}