184 lines
5.3 KiB
TypeScript
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',
|
|
);
|
|
}
|
|
}
|