135 lines
4.8 KiB
TypeScript
135 lines
4.8 KiB
TypeScript
import { Controller, Get, Post, Patch, Delete, Body, Param, ParseIntPipe } from '@nestjs/common';
|
|
import {
|
|
ApiTags,
|
|
ApiOperation,
|
|
ApiBearerAuth,
|
|
ApiOkResponse,
|
|
ApiCreatedResponse,
|
|
ApiExtraModels,
|
|
getSchemaPath,
|
|
} from '@nestjs/swagger';
|
|
import { PortfolioService } from './portfolio.service';
|
|
import { CreatePortfolioDto } from './dto/create-portfolio.dto';
|
|
import { UpdatePortfolioDto } from './dto/update-portfolio.dto';
|
|
import { AddPositionDto } from './dto/add-position.dto';
|
|
import { UpdatePositionDto } from './dto/update-position.dto';
|
|
import { CurrentUser } from '../auth/decorators/current-user.decorator';
|
|
import {
|
|
AnalyticsEnvelopeDto,
|
|
PortfolioDetailEnvelopeDto,
|
|
PortfolioEnvelopeDto,
|
|
PortfolioListEnvelopeDto,
|
|
PortfolioResponseMetaDto,
|
|
PositionEnvelopeDto,
|
|
} from './dto/portfolio-envelope.dto';
|
|
|
|
const nullDataEnvelopeSchema = {
|
|
type: 'object',
|
|
properties: {
|
|
data: {
|
|
type: 'null',
|
|
},
|
|
meta: {
|
|
$ref: getSchemaPath(PortfolioResponseMetaDto),
|
|
},
|
|
},
|
|
required: ['data', 'meta'],
|
|
};
|
|
|
|
@ApiTags('Portfolios')
|
|
@ApiBearerAuth()
|
|
@ApiExtraModels(PortfolioResponseMetaDto)
|
|
@Controller('portfolios')
|
|
export class PortfolioController {
|
|
constructor(private readonly portfolioService: PortfolioService) {}
|
|
|
|
@Get()
|
|
@ApiOperation({ summary: 'Get all portfolios for current user' })
|
|
@ApiOkResponse({ type: PortfolioListEnvelopeDto })
|
|
async findAll(@CurrentUser() user: { sub: number }) {
|
|
const portfolios = await this.portfolioService.findAll(user.sub);
|
|
return { data: portfolios, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Post()
|
|
@ApiOperation({ summary: 'Create a new portfolio' })
|
|
@ApiCreatedResponse({ type: PortfolioEnvelopeDto })
|
|
async create(@CurrentUser() user: { sub: number }, @Body() dto: CreatePortfolioDto) {
|
|
const portfolio = await this.portfolioService.create(user.sub, dto);
|
|
return { data: portfolio, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Get(':id')
|
|
@ApiOperation({ summary: 'Get portfolio details with positions and prices' })
|
|
@ApiOkResponse({ type: PortfolioDetailEnvelopeDto })
|
|
async findOne(@CurrentUser() user: { sub: number }, @Param('id', ParseIntPipe) id: number) {
|
|
const portfolio = await this.portfolioService.findOne(user.sub, id);
|
|
return { data: portfolio, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Patch(':id')
|
|
@ApiOperation({ summary: 'Update portfolio' })
|
|
@ApiOkResponse({ type: PortfolioEnvelopeDto })
|
|
async update(
|
|
@CurrentUser() user: { sub: number },
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() dto: UpdatePortfolioDto,
|
|
) {
|
|
const portfolio = await this.portfolioService.update(user.sub, id, dto);
|
|
return { data: portfolio, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Delete(':id')
|
|
@ApiOperation({ summary: 'Delete portfolio' })
|
|
@ApiOkResponse({ schema: nullDataEnvelopeSchema })
|
|
async remove(@CurrentUser() user: { sub: number }, @Param('id', ParseIntPipe) id: number) {
|
|
await this.portfolioService.remove(user.sub, id);
|
|
return { data: null, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Post(':id/positions')
|
|
@ApiOperation({ summary: 'Add position to portfolio' })
|
|
@ApiCreatedResponse({ type: PositionEnvelopeDto })
|
|
async addPosition(
|
|
@CurrentUser() user: { sub: number },
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() dto: AddPositionDto,
|
|
) {
|
|
const position = await this.portfolioService.addPosition(user.sub, id, dto);
|
|
return { data: position, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Patch(':id/positions/:positionId')
|
|
@ApiOperation({ summary: 'Update position' })
|
|
@ApiOkResponse({ type: PositionEnvelopeDto })
|
|
async updatePosition(
|
|
@CurrentUser() user: { sub: number },
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Param('positionId', ParseIntPipe) positionId: number,
|
|
@Body() dto: UpdatePositionDto,
|
|
) {
|
|
const position = await this.portfolioService.updatePosition(user.sub, id, positionId, dto);
|
|
return { data: position, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Get(':id/analytics')
|
|
@ApiOperation({ summary: 'Get portfolio analytics with PnL' })
|
|
@ApiOkResponse({ type: AnalyticsEnvelopeDto })
|
|
async getAnalytics(@CurrentUser() user: { sub: number }, @Param('id', ParseIntPipe) id: number) {
|
|
const result = await this.portfolioService.getAnalytics(user.sub, id);
|
|
return { data: result, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
|
|
@Delete(':id/positions/:positionId')
|
|
@ApiOperation({ summary: 'Remove position from portfolio' })
|
|
@ApiOkResponse({ schema: nullDataEnvelopeSchema })
|
|
async removePosition(
|
|
@CurrentUser() user: { sub: number },
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Param('positionId', ParseIntPipe) positionId: number,
|
|
) {
|
|
await this.portfolioService.removePosition(user.sub, id, positionId);
|
|
return { data: null, meta: { cachedAt: null, fromCache: false } };
|
|
}
|
|
}
|