comments implement
This commit is contained in:
1 parent
9e0bef2527
commit
4b6ecfbf5d
41 files changed
+1350
-13
No files matched your search
@@ -0,0 +1,101 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
ForbiddenException,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
type MessageEvent,
|
||||
NotFoundException,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
Sse,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { Session, type UserSession } from '@thallesp/nestjs-better-auth';
|
||||
import { map, type Observable } from 'rxjs';
|
||||
import { CreateCommentUseCase } from '../../application/use-cases/create-comment/create-comment.use-case.js';
|
||||
import { ListCommentsUseCase } from '../../application/use-cases/list-comments/list-comments.use-case.js';
|
||||
import { UpdateCommentUseCase } from '../../application/use-cases/update-comment/update-comment.use-case.js';
|
||||
import { DeleteCommentUseCase } from '../../application/use-cases/delete-comment/delete-comment.use-case.js';
|
||||
import { WatchIssueCommentsUseCase } from '../../application/use-cases/watch-issue-comments/watch-issue-comments.use-case.js';
|
||||
import { CreateCommentCommand } from '../../application/use-cases/create-comment/create-comment.command.js';
|
||||
import { ListCommentsQuery } from '../../application/use-cases/list-comments/list-comments.query.js';
|
||||
import { UpdateCommentCommand } from '../../application/use-cases/update-comment/update-comment.command.js';
|
||||
import { DeleteCommentCommand } from '../../application/use-cases/delete-comment/delete-comment.command.js';
|
||||
import { WatchIssueCommentsQuery } from '../../application/use-cases/watch-issue-comments/watch-issue-comments.query.js';
|
||||
import { CreateCommentRequestDto } from '../dto/create-comment.dto.js';
|
||||
import { UpdateCommentRequestDto } from '../dto/update-comment.dto.js';
|
||||
import { ListCommentsDto } from '../dto/list-comments.dto.js';
|
||||
import { CommentNotFoundException } from '../../domain/exceptions/comment-not-found.exception.js';
|
||||
import { CommentForbiddenException } from '../../domain/exceptions/comment-forbidden.exception.js';
|
||||
import { IssueNotFoundException } from '../../../issue/domain/exceptions/issue-not-found.exception.js';
|
||||
import { IssueVisibleGuard } from '../guards/issue-visible.guard.js';
|
||||
|
||||
@Controller()
|
||||
export class CommentController {
|
||||
constructor(
|
||||
private readonly createCommentUseCase: CreateCommentUseCase,
|
||||
private readonly listCommentsUseCase: ListCommentsUseCase,
|
||||
private readonly updateCommentUseCase: UpdateCommentUseCase,
|
||||
private readonly deleteCommentUseCase: DeleteCommentUseCase,
|
||||
private readonly watchIssueCommentsUseCase: WatchIssueCommentsUseCase,
|
||||
) {}
|
||||
|
||||
@Get('issues/:issueId/comments')
|
||||
list(@Param('issueId', ParseIntPipe) issueId: number, @Query() query: ListCommentsDto, @Session() session: UserSession) {
|
||||
return this.handleErrors(() =>
|
||||
this.listCommentsUseCase.execute(new ListCommentsQuery(issueId, session.user.id, Number(query.limit), query.cursor)),
|
||||
);
|
||||
}
|
||||
|
||||
@Post('issues/:issueId/comments')
|
||||
create(@Param('issueId', ParseIntPipe) issueId: number, @Body() dto: CreateCommentRequestDto, @Session() session: UserSession) {
|
||||
return this.handleErrors(() => this.createCommentUseCase.execute(new CreateCommentCommand(issueId, session.user.id, dto.content)));
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE-поток комментариев одной задачи: клиент открывает его на странице
|
||||
* задачи и закрывает при уходе. После переподключения клиенту стоит
|
||||
* перезапросить список — события, пришедшие во время обрыва, не повторяются.
|
||||
* Доступ проверяет IssueVisibleGuard — так клиент без доступа получает 404.
|
||||
*/
|
||||
@Sse('issues/:issueId/comments/events')
|
||||
@UseGuards(IssueVisibleGuard)
|
||||
events(@Param('issueId', ParseIntPipe) issueId: number, @Session() session: UserSession): Promise<Observable<MessageEvent>> {
|
||||
return this.handleErrors(async () => {
|
||||
const events$ = await this.watchIssueCommentsUseCase.execute(new WatchIssueCommentsQuery(issueId, session.user.id));
|
||||
return events$.pipe(map((event) => ({ type: event.type, data: event.comment })));
|
||||
});
|
||||
}
|
||||
|
||||
@Patch('comments/:id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateCommentRequestDto, @Session() session: UserSession) {
|
||||
return this.handleErrors(() => this.updateCommentUseCase.execute(new UpdateCommentCommand(id, session.user.id, dto.content)));
|
||||
}
|
||||
|
||||
@Delete('comments/:id')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
async delete(@Param('id') id: string, @Session() session: UserSession) {
|
||||
await this.handleErrors(() => this.deleteCommentUseCase.execute(new DeleteCommentCommand(id, session.user.id)));
|
||||
}
|
||||
|
||||
/** Транслирует доменные исключения в HTTP-ответ — это забота presentation-слоя */
|
||||
private async handleErrors<T>(fn: () => Promise<T>): Promise<T> {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
if (error instanceof CommentNotFoundException || error instanceof IssueNotFoundException) {
|
||||
throw new NotFoundException(error.message);
|
||||
}
|
||||
if (error instanceof CommentForbiddenException) {
|
||||
throw new ForbiddenException(error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user