From 7a9cc082fca3bdd28efd8207989ccf83fc17e318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= <68596831+Hakkush-07@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:35:21 +0300 Subject: [PATCH 1/6] Comment entity and writeComment and likeComment endpoints --- ludos/backend/src/app.module.ts | 5 ++- .../src/controllers/user.controller.ts | 44 +++++++++++++++++++ .../dtos/comment/request/like-comment.dto.ts | 8 ++++ .../dtos/comment/request/write-comment.dto.ts | 12 +++++ ludos/backend/src/entities/comment.entity.ts | 24 ++++++++++ .../src/repositories/comment.repository.ts | 26 +++++++++++ .../services/config/typeorm-config.service.ts | 3 +- ludos/backend/src/services/user.service.ts | 36 +++++++++++++++ ludos/backend/test/user.controller.spec.ts | 2 + 9 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 ludos/backend/src/dtos/comment/request/like-comment.dto.ts create mode 100644 ludos/backend/src/dtos/comment/request/write-comment.dto.ts create mode 100644 ludos/backend/src/entities/comment.entity.ts create mode 100644 ludos/backend/src/repositories/comment.repository.ts diff --git a/ludos/backend/src/app.module.ts b/ludos/backend/src/app.module.ts index f7b7b4ac..6691a879 100644 --- a/ludos/backend/src/app.module.ts +++ b/ludos/backend/src/app.module.ts @@ -15,8 +15,10 @@ import { GameController } from './controllers/game.controller'; import { GameService } from './services/game.service'; import { GameRepository } from './repositories/game.repository'; import { Game } from './entities/game.entity'; +import { Comment } from './entities/comment.entity'; import { TokenDecoderMiddleware } from './middlewares/tokenDecoder.middleware'; import { ResetPasswordRepository } from './repositories/reset-password.repository'; +import { CommentRepository } from './repositories/comment.repository'; import { S3Service } from './services/s3.service'; import { S3Controller } from './controllers/s3.controller'; @@ -33,7 +35,7 @@ import { S3Controller } from './controllers/s3.controller'; useClass: TypeOrmConfigService, inject: [TypeOrmConfigService], }), - TypeOrmModule.forFeature([User, Game, ResetPassword]), + TypeOrmModule.forFeature([User, Game, ResetPassword, Comment]), ], controllers: [AppController, UserController, GameController, S3Controller], providers: [ @@ -44,6 +46,7 @@ import { S3Controller } from './controllers/s3.controller'; GameService, ResetPasswordRepository, S3Service, + CommentRepository, ], }) export class AppModule implements NestModule { diff --git a/ludos/backend/src/controllers/user.controller.ts b/ludos/backend/src/controllers/user.controller.ts index dd75196f..e173a9f9 100644 --- a/ludos/backend/src/controllers/user.controller.ts +++ b/ludos/backend/src/controllers/user.controller.ts @@ -27,6 +27,8 @@ import { VerifyCodeDto } from '../dtos/user/request/verify-code.dto'; import { ChangePasswordResponseDto } from '../dtos/user/response/change-password-response.dto'; import { ChangePasswordDto } from '../dtos/user/request/change-password.dto'; import { EditUserInfoDto } from '../dtos/user/request/edit-info.dto'; +import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; +import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; import { UserService } from '../services/user.service'; import { AuthGuard } from '../services/guards/auth.guard'; import { AuthorizedRequest } from '../interfaces/common/authorized-request.interface'; @@ -154,4 +156,46 @@ export class UserController { public async getUserInfoById(@Req() req: AuthorizedRequest) { return await this.userService.getUserInfo(req.user.id); } + + @ApiOperation({ summary: 'Comment on a post' }) + @ApiOkResponse({ + description: 'Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/write-comment') + public async writeComment( + @Req() req: AuthorizedRequest, + @Body() input: WriteCommentDto, + ) { + await this.userService.writeComment(req.user.id, input); + } + + @ApiOperation({ summary: 'Like a comment' }) + @ApiOkResponse({ + description: 'Like Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/like-comment') + public async likeComment( + @Req() req: AuthorizedRequest, + @Body() input: LikeCommentDto, + ) { + await this.userService.likeComment(req.user.id, input); + } } diff --git a/ludos/backend/src/dtos/comment/request/like-comment.dto.ts b/ludos/backend/src/dtos/comment/request/like-comment.dto.ts new file mode 100644 index 00000000..24406661 --- /dev/null +++ b/ludos/backend/src/dtos/comment/request/like-comment.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +export class LikeCommentDto { + @ApiProperty() + @IsString() + commentId: string; +} + diff --git a/ludos/backend/src/dtos/comment/request/write-comment.dto.ts b/ludos/backend/src/dtos/comment/request/write-comment.dto.ts new file mode 100644 index 00000000..4a9e95f9 --- /dev/null +++ b/ludos/backend/src/dtos/comment/request/write-comment.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +export class WriteCommentDto { + @ApiProperty() + @IsString() + postId: string; + + @ApiProperty() + @IsString() + text: string; +} + diff --git a/ludos/backend/src/entities/comment.entity.ts b/ludos/backend/src/entities/comment.entity.ts new file mode 100644 index 00000000..094fbf5b --- /dev/null +++ b/ludos/backend/src/entities/comment.entity.ts @@ -0,0 +1,24 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, +} from 'typeorm'; +//import { User } from '../entities/user.entity'; + +@Entity('comments') +export class Comment { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + author: string; //User; + + @Column() + postId: string; + + @Column() + text: string; + + @Column() + likes: number; +} diff --git a/ludos/backend/src/repositories/comment.repository.ts b/ludos/backend/src/repositories/comment.repository.ts new file mode 100644 index 00000000..f81c508e --- /dev/null +++ b/ludos/backend/src/repositories/comment.repository.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { Comment } from '../entities/comment.entity'; +import { Repository, DataSource } from 'typeorm'; + +@Injectable() +export class CommentRepository extends Repository { + constructor(dataSource: DataSource) { + super(Comment, dataSource.createEntityManager()); + } + + public async createComment(input: Partial): Promise { + let comment = this.create(input); + await this.insert(comment); + return comment; + } + + public findCommentById(id: string): Promise { + return this.findOneBy({ id }); + } + + public async incrementLikeCount(commentId: string) { + let comment = await this.findCommentById(commentId); + comment.likes += 1; + await this.save(comment); + } +} diff --git a/ludos/backend/src/services/config/typeorm-config.service.ts b/ludos/backend/src/services/config/typeorm-config.service.ts index 619f5723..19604a93 100644 --- a/ludos/backend/src/services/config/typeorm-config.service.ts +++ b/ludos/backend/src/services/config/typeorm-config.service.ts @@ -4,6 +4,7 @@ import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm'; import { User } from '../../entities/user.entity'; import { ResetPassword } from '../../entities/reset-password.entity'; import { Game } from '../../entities/game.entity'; +import { Comment } from '../../entities/comment.entity'; @Injectable() export class TypeOrmConfigService implements TypeOrmOptionsFactory { @@ -17,7 +18,7 @@ export class TypeOrmConfigService implements TypeOrmOptionsFactory { password: this.configService.get('DB_PASSWORD'), port: this.configService.get('DB_PORT'), database: this.configService.get('DB_NAME'), - entities: [User, ResetPassword, Game], + entities: [User, ResetPassword, Game, Comment], synchronize: true, }; } diff --git a/ludos/backend/src/services/user.service.ts b/ludos/backend/src/services/user.service.ts index 6efed627..0f16a4d4 100644 --- a/ludos/backend/src/services/user.service.ts +++ b/ludos/backend/src/services/user.service.ts @@ -20,9 +20,12 @@ import { ChangePasswordResponseDto } from '../dtos/user/response/change-password import { LoginResponseDto } from '../dtos/user/response/login-response.dto'; import { RegisterResponseDto } from '../dtos/user/response/register-response.dto'; import { EditUserInfoDto } from '../dtos/user/request/edit-info.dto'; +import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; +import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; import { Payload } from '../interfaces/user/payload.interface'; import { ResetPasswordRepository } from '../repositories/reset-password.repository'; import { UserRepository } from '../repositories/user.repository'; +import { CommentRepository } from '../repositories/comment.repository'; import { GetUserInfoResponseDto } from '../dtos/user/response/get-user-info-response.dto'; @Injectable() @@ -30,6 +33,7 @@ export class UserService { constructor( private readonly userRepository: UserRepository, private readonly resetPasswordRepository: ResetPasswordRepository, + private readonly commentRepository: CommentRepository, private readonly jwtService: JwtService, private readonly configService: ConfigService, ) {} @@ -202,4 +206,36 @@ export class UserService { return response; } + + public async writeComment(userId: string, writeCommentDto: WriteCommentDto) { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this email', + HttpStatus.FORBIDDEN, + ); + } + + let comment = { + author: userId, //user, + text: writeCommentDto.text, + postId: writeCommentDto.postId, + likes: 0, + } + let c = await this.commentRepository.createComment(comment); + } + + public async likeComment(userId: string, likeCommentDto: LikeCommentDto) { + let comment = await this.commentRepository.findCommentById(likeCommentDto.commentId); + + if (!comment) { + throw new HttpException( + 'No comment found with this id', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.incrementLikeCount(likeCommentDto.commentId); + } } diff --git a/ludos/backend/test/user.controller.spec.ts b/ludos/backend/test/user.controller.spec.ts index 781e9cb6..1431a52d 100644 --- a/ludos/backend/test/user.controller.spec.ts +++ b/ludos/backend/test/user.controller.spec.ts @@ -14,6 +14,7 @@ import { S3Service } from '../src/services/s3.service'; import { ResetPassword } from '../src/entities/reset-password.entity'; import { ResetDto } from '../src/dtos/user/request/reset.dto'; import { ResetPasswordRepository } from '../src/repositories/reset-password.repository'; +import { CommentRepository } from '../src/repositories/comment.repository'; describe('UserController', () => { let userController: UserController; @@ -38,6 +39,7 @@ describe('UserController', () => { S3Service, UserRepository, ResetPasswordRepository, + CommentRepository, { provide: DataSource, useValue: dataSource, From d88f00e6a9b9e8382bb2902da04d5cfb0b69e1c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= <68596831+Hakkush-07@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:16:34 +0300 Subject: [PATCH 2/6] add timestamp, parentId fields and dislikeComment endpoint to Comment entity --- .../src/controllers/user.controller.ts | 22 +++++++++++++++++ .../comment/request/dislike-comment.dto.ts | 8 +++++++ .../dtos/comment/request/write-comment.dto.ts | 2 +- ludos/backend/src/entities/comment.entity.ts | 8 ++++++- .../src/repositories/comment.repository.ts | 6 +++++ ludos/backend/src/services/user.service.ts | 24 +++++++++++++++++-- 6 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts diff --git a/ludos/backend/src/controllers/user.controller.ts b/ludos/backend/src/controllers/user.controller.ts index e173a9f9..61e63641 100644 --- a/ludos/backend/src/controllers/user.controller.ts +++ b/ludos/backend/src/controllers/user.controller.ts @@ -29,6 +29,7 @@ import { ChangePasswordDto } from '../dtos/user/request/change-password.dto'; import { EditUserInfoDto } from '../dtos/user/request/edit-info.dto'; import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; +import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; import { UserService } from '../services/user.service'; import { AuthGuard } from '../services/guards/auth.guard'; import { AuthorizedRequest } from '../interfaces/common/authorized-request.interface'; @@ -198,4 +199,25 @@ export class UserController { ) { await this.userService.likeComment(req.user.id, input); } + + @ApiOperation({ summary: 'Dislike a comment' }) + @ApiOkResponse({ + description: 'Dislike Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/dislike-comment') + public async dislikeComment( + @Req() req: AuthorizedRequest, + @Body() input: DislikeCommentDto, + ) { + await this.userService.dislikeComment(req.user.id, input); + } } diff --git a/ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts b/ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts new file mode 100644 index 00000000..6dcb5737 --- /dev/null +++ b/ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +export class DislikeCommentDto { + @ApiProperty() + @IsString() + commentId: string; +} + diff --git a/ludos/backend/src/dtos/comment/request/write-comment.dto.ts b/ludos/backend/src/dtos/comment/request/write-comment.dto.ts index 4a9e95f9..32a9a635 100644 --- a/ludos/backend/src/dtos/comment/request/write-comment.dto.ts +++ b/ludos/backend/src/dtos/comment/request/write-comment.dto.ts @@ -3,7 +3,7 @@ import { IsString } from 'class-validator'; export class WriteCommentDto { @ApiProperty() @IsString() - postId: string; + parentId: string; @ApiProperty() @IsString() diff --git a/ludos/backend/src/entities/comment.entity.ts b/ludos/backend/src/entities/comment.entity.ts index 094fbf5b..60d9c001 100644 --- a/ludos/backend/src/entities/comment.entity.ts +++ b/ludos/backend/src/entities/comment.entity.ts @@ -14,11 +14,17 @@ export class Comment { author: string; //User; @Column() - postId: string; + timestamp: Date; + + @Column() + parentId: string; @Column() text: string; @Column() likes: number; + + @Column() + dislikes: number; } diff --git a/ludos/backend/src/repositories/comment.repository.ts b/ludos/backend/src/repositories/comment.repository.ts index f81c508e..41aff425 100644 --- a/ludos/backend/src/repositories/comment.repository.ts +++ b/ludos/backend/src/repositories/comment.repository.ts @@ -23,4 +23,10 @@ export class CommentRepository extends Repository { comment.likes += 1; await this.save(comment); } + + public async incrementDislikeCount(commentId: string) { + let comment = await this.findCommentById(commentId); + comment.dislikes += 1; + await this.save(comment); + } } diff --git a/ludos/backend/src/services/user.service.ts b/ludos/backend/src/services/user.service.ts index 0f16a4d4..f271b25f 100644 --- a/ludos/backend/src/services/user.service.ts +++ b/ludos/backend/src/services/user.service.ts @@ -22,6 +22,7 @@ import { RegisterResponseDto } from '../dtos/user/response/register-response.dto import { EditUserInfoDto } from '../dtos/user/request/edit-info.dto'; import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; +import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; import { Payload } from '../interfaces/user/payload.interface'; import { ResetPasswordRepository } from '../repositories/reset-password.repository'; import { UserRepository } from '../repositories/user.repository'; @@ -217,13 +218,19 @@ export class UserService { ); } + // check parent id + // post / comment / game review + // parent id should be the identifier of one of the above + let comment = { author: userId, //user, text: writeCommentDto.text, - postId: writeCommentDto.postId, + parentId: writeCommentDto.parentId, likes: 0, + dislikes: 0, + timestamp: new Date(), } - let c = await this.commentRepository.createComment(comment); + await this.commentRepository.createComment(comment); } public async likeComment(userId: string, likeCommentDto: LikeCommentDto) { @@ -238,4 +245,17 @@ export class UserService { await this.commentRepository.incrementLikeCount(likeCommentDto.commentId); } + + public async dislikeComment(userId: string, dislikeCommentDto: DislikeCommentDto) { + let comment = await this.commentRepository.findCommentById(dislikeCommentDto.commentId); + + if (!comment) { + throw new HttpException( + 'No comment found with this id', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.incrementDislikeCount(dislikeCommentDto.commentId); + } } From 725db2f7676fd932c5f79c3771156ceb5c5fefbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= <68596831+Hakkush-07@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:32:27 +0300 Subject: [PATCH 3/6] take comment related things to separate controller and service --- ludos/backend/src/app.module.ts | 5 +- .../src/controllers/comment.controller.ts | 91 +++++++++++++++++++ .../src/controllers/user.controller.ts | 63 ------------- ludos/backend/src/services/comment.service.ts | 69 ++++++++++++++ ludos/backend/src/services/user.service.ts | 55 ----------- ludos/backend/test/user.controller.spec.ts | 2 - 6 files changed, 164 insertions(+), 121 deletions(-) create mode 100644 ludos/backend/src/controllers/comment.controller.ts create mode 100644 ludos/backend/src/services/comment.service.ts diff --git a/ludos/backend/src/app.module.ts b/ludos/backend/src/app.module.ts index 6691a879..d6729502 100644 --- a/ludos/backend/src/app.module.ts +++ b/ludos/backend/src/app.module.ts @@ -21,6 +21,8 @@ import { ResetPasswordRepository } from './repositories/reset-password.repositor import { CommentRepository } from './repositories/comment.repository'; import { S3Service } from './services/s3.service'; import { S3Controller } from './controllers/s3.controller'; +import { CommentService } from './services/comment.service'; +import { CommentController } from './controllers/comment.controller'; @Module({ imports: [ @@ -37,7 +39,7 @@ import { S3Controller } from './controllers/s3.controller'; }), TypeOrmModule.forFeature([User, Game, ResetPassword, Comment]), ], - controllers: [AppController, UserController, GameController, S3Controller], + controllers: [AppController, UserController, GameController, S3Controller, CommentController], providers: [ AppService, UserRepository, @@ -47,6 +49,7 @@ import { S3Controller } from './controllers/s3.controller'; ResetPasswordRepository, S3Service, CommentRepository, + CommentService, ], }) export class AppModule implements NestModule { diff --git a/ludos/backend/src/controllers/comment.controller.ts b/ludos/backend/src/controllers/comment.controller.ts new file mode 100644 index 00000000..57685f34 --- /dev/null +++ b/ludos/backend/src/controllers/comment.controller.ts @@ -0,0 +1,91 @@ +import { + Body, + Controller, + HttpCode, + Post, + Req, + UseGuards, +} from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiOkResponse, + ApiOperation, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; +import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; +import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; +import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; +import { CommentService } from '../services/comment.service'; +import { AuthGuard } from '../services/guards/auth.guard'; +import { AuthorizedRequest } from '../interfaces/common/authorized-request.interface'; + +@ApiTags('comment') +@Controller('comment') +export class CommentController { + constructor(private readonly commentService: CommentService) { } + + @ApiOperation({ summary: 'Comment on a post' }) + @ApiOkResponse({ + description: 'Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/write-comment') + public async writeComment( + @Req() req: AuthorizedRequest, + @Body() input: WriteCommentDto, + ) { + await this.commentService.writeComment(req.user.id, input); + } + + @ApiOperation({ summary: 'Like a comment' }) + @ApiOkResponse({ + description: 'Like Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/like-comment') + public async likeComment( + @Req() req: AuthorizedRequest, + @Body() input: LikeCommentDto, + ) { + await this.commentService.likeComment(req.user.id, input); + } + + @ApiOperation({ summary: 'Dislike a comment' }) + @ApiOkResponse({ + description: 'Dislike Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/dislike-comment') + public async dislikeComment( + @Req() req: AuthorizedRequest, + @Body() input: DislikeCommentDto, + ) { + await this.commentService.dislikeComment(req.user.id, input); + } +} diff --git a/ludos/backend/src/controllers/user.controller.ts b/ludos/backend/src/controllers/user.controller.ts index 61e63641..d6793f27 100644 --- a/ludos/backend/src/controllers/user.controller.ts +++ b/ludos/backend/src/controllers/user.controller.ts @@ -157,67 +157,4 @@ export class UserController { public async getUserInfoById(@Req() req: AuthorizedRequest) { return await this.userService.getUserInfo(req.user.id); } - - @ApiOperation({ summary: 'Comment on a post' }) - @ApiOkResponse({ - description: 'Comment', - }) - @ApiUnauthorizedResponse({ - description: 'Invalid Credentials', - }) - @ApiBadRequestResponse({ - description: 'Bad Request', - }) - @HttpCode(200) - @ApiBearerAuth() - @UseGuards(AuthGuard) - @Post('/write-comment') - public async writeComment( - @Req() req: AuthorizedRequest, - @Body() input: WriteCommentDto, - ) { - await this.userService.writeComment(req.user.id, input); - } - - @ApiOperation({ summary: 'Like a comment' }) - @ApiOkResponse({ - description: 'Like Comment', - }) - @ApiUnauthorizedResponse({ - description: 'Invalid Credentials', - }) - @ApiBadRequestResponse({ - description: 'Bad Request', - }) - @HttpCode(200) - @ApiBearerAuth() - @UseGuards(AuthGuard) - @Post('/like-comment') - public async likeComment( - @Req() req: AuthorizedRequest, - @Body() input: LikeCommentDto, - ) { - await this.userService.likeComment(req.user.id, input); - } - - @ApiOperation({ summary: 'Dislike a comment' }) - @ApiOkResponse({ - description: 'Dislike Comment', - }) - @ApiUnauthorizedResponse({ - description: 'Invalid Credentials', - }) - @ApiBadRequestResponse({ - description: 'Bad Request', - }) - @HttpCode(200) - @ApiBearerAuth() - @UseGuards(AuthGuard) - @Post('/dislike-comment') - public async dislikeComment( - @Req() req: AuthorizedRequest, - @Body() input: DislikeCommentDto, - ) { - await this.userService.dislikeComment(req.user.id, input); - } } diff --git a/ludos/backend/src/services/comment.service.ts b/ludos/backend/src/services/comment.service.ts new file mode 100644 index 00000000..5702cf7e --- /dev/null +++ b/ludos/backend/src/services/comment.service.ts @@ -0,0 +1,69 @@ +import { + HttpException, + HttpStatus, + Injectable, +} from '@nestjs/common'; +import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; +import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; +import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; +import { UserRepository } from '../repositories/user.repository'; +import { CommentRepository } from '../repositories/comment.repository'; + +@Injectable() +export class CommentService { + constructor( + private readonly userRepository: UserRepository, + private readonly commentRepository: CommentRepository, + ) {} + + public async writeComment(userId: string, writeCommentDto: WriteCommentDto) { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this email', + HttpStatus.FORBIDDEN, + ); + } + + // check parent id + // post / comment / game review + // parent id should be the identifier of one of the above + + let comment = { + author: userId, //user, + text: writeCommentDto.text, + parentId: writeCommentDto.parentId, + likes: 0, + dislikes: 0, + timestamp: new Date(), + } + await this.commentRepository.createComment(comment); + } + + public async likeComment(userId: string, likeCommentDto: LikeCommentDto) { + let comment = await this.commentRepository.findCommentById(likeCommentDto.commentId); + + if (!comment) { + throw new HttpException( + 'No comment found with this id', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.incrementLikeCount(likeCommentDto.commentId); + } + + public async dislikeComment(userId: string, dislikeCommentDto: DislikeCommentDto) { + let comment = await this.commentRepository.findCommentById(dislikeCommentDto.commentId); + + if (!comment) { + throw new HttpException( + 'No comment found with this id', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.incrementDislikeCount(dislikeCommentDto.commentId); + } +} diff --git a/ludos/backend/src/services/user.service.ts b/ludos/backend/src/services/user.service.ts index f271b25f..4ff7846a 100644 --- a/ludos/backend/src/services/user.service.ts +++ b/ludos/backend/src/services/user.service.ts @@ -20,9 +20,6 @@ import { ChangePasswordResponseDto } from '../dtos/user/response/change-password import { LoginResponseDto } from '../dtos/user/response/login-response.dto'; import { RegisterResponseDto } from '../dtos/user/response/register-response.dto'; import { EditUserInfoDto } from '../dtos/user/request/edit-info.dto'; -import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; -import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; -import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; import { Payload } from '../interfaces/user/payload.interface'; import { ResetPasswordRepository } from '../repositories/reset-password.repository'; import { UserRepository } from '../repositories/user.repository'; @@ -34,7 +31,6 @@ export class UserService { constructor( private readonly userRepository: UserRepository, private readonly resetPasswordRepository: ResetPasswordRepository, - private readonly commentRepository: CommentRepository, private readonly jwtService: JwtService, private readonly configService: ConfigService, ) {} @@ -207,55 +203,4 @@ export class UserService { return response; } - - public async writeComment(userId: string, writeCommentDto: WriteCommentDto) { - let user = await this.userRepository.findUserById(userId); - - if (!user) { - throw new HttpException( - 'No user found with this email', - HttpStatus.FORBIDDEN, - ); - } - - // check parent id - // post / comment / game review - // parent id should be the identifier of one of the above - - let comment = { - author: userId, //user, - text: writeCommentDto.text, - parentId: writeCommentDto.parentId, - likes: 0, - dislikes: 0, - timestamp: new Date(), - } - await this.commentRepository.createComment(comment); - } - - public async likeComment(userId: string, likeCommentDto: LikeCommentDto) { - let comment = await this.commentRepository.findCommentById(likeCommentDto.commentId); - - if (!comment) { - throw new HttpException( - 'No comment found with this id', - HttpStatus.FORBIDDEN, - ); - } - - await this.commentRepository.incrementLikeCount(likeCommentDto.commentId); - } - - public async dislikeComment(userId: string, dislikeCommentDto: DislikeCommentDto) { - let comment = await this.commentRepository.findCommentById(dislikeCommentDto.commentId); - - if (!comment) { - throw new HttpException( - 'No comment found with this id', - HttpStatus.FORBIDDEN, - ); - } - - await this.commentRepository.incrementDislikeCount(dislikeCommentDto.commentId); - } } diff --git a/ludos/backend/test/user.controller.spec.ts b/ludos/backend/test/user.controller.spec.ts index 1431a52d..781e9cb6 100644 --- a/ludos/backend/test/user.controller.spec.ts +++ b/ludos/backend/test/user.controller.spec.ts @@ -14,7 +14,6 @@ import { S3Service } from '../src/services/s3.service'; import { ResetPassword } from '../src/entities/reset-password.entity'; import { ResetDto } from '../src/dtos/user/request/reset.dto'; import { ResetPasswordRepository } from '../src/repositories/reset-password.repository'; -import { CommentRepository } from '../src/repositories/comment.repository'; describe('UserController', () => { let userController: UserController; @@ -39,7 +38,6 @@ describe('UserController', () => { S3Service, UserRepository, ResetPasswordRepository, - CommentRepository, { provide: DataSource, useValue: dataSource, From ff27aa9f0e84f21d442defdded2eb7f7039f5895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= <68596831+Hakkush-07@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:46:36 +0300 Subject: [PATCH 4/6] add deleteComment and editComment endpoints --- .../src/controllers/comment.controller.ts | 44 +++++++++++++++++++ .../comment/request/delete-comment.dto.ts | 8 ++++ .../dtos/comment/request/edit-comment.dto.ts | 12 +++++ ludos/backend/src/entities/comment.entity.ts | 3 ++ .../src/repositories/comment.repository.ts | 12 +++++ ludos/backend/src/services/comment.service.ts | 28 ++++++++++++ 6 files changed, 107 insertions(+) create mode 100644 ludos/backend/src/dtos/comment/request/delete-comment.dto.ts create mode 100644 ludos/backend/src/dtos/comment/request/edit-comment.dto.ts diff --git a/ludos/backend/src/controllers/comment.controller.ts b/ludos/backend/src/controllers/comment.controller.ts index 57685f34..14c3f5fe 100644 --- a/ludos/backend/src/controllers/comment.controller.ts +++ b/ludos/backend/src/controllers/comment.controller.ts @@ -17,6 +17,8 @@ import { import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; +import { DeleteCommentDto } from '../dtos/comment/request/delete-comment.dto'; +import { EditCommentDto } from '../dtos/comment/request/edit-comment.dto'; import { CommentService } from '../services/comment.service'; import { AuthGuard } from '../services/guards/auth.guard'; import { AuthorizedRequest } from '../interfaces/common/authorized-request.interface'; @@ -88,4 +90,46 @@ export class CommentController { ) { await this.commentService.dislikeComment(req.user.id, input); } + + @ApiOperation({ summary: 'Delete a comment' }) + @ApiOkResponse({ + description: 'Deleted Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/delete-comment') + public async deleteComment( + @Req() req: AuthorizedRequest, + @Body() input: DeleteCommentDto, + ) { + await this.commentService.deleteComment(req.user.id, input); + } + + @ApiOperation({ summary: 'Edit a comment' }) + @ApiOkResponse({ + description: 'Edited Comment', + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Post('/edit-comment') + public async editComment( + @Req() req: AuthorizedRequest, + @Body() input: EditCommentDto, + ) { + await this.commentService.editComment(req.user.id, input); + } } diff --git a/ludos/backend/src/dtos/comment/request/delete-comment.dto.ts b/ludos/backend/src/dtos/comment/request/delete-comment.dto.ts new file mode 100644 index 00000000..c9942957 --- /dev/null +++ b/ludos/backend/src/dtos/comment/request/delete-comment.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +export class DeleteCommentDto { + @ApiProperty() + @IsString() + commentId: string; +} + diff --git a/ludos/backend/src/dtos/comment/request/edit-comment.dto.ts b/ludos/backend/src/dtos/comment/request/edit-comment.dto.ts new file mode 100644 index 00000000..df1c4c69 --- /dev/null +++ b/ludos/backend/src/dtos/comment/request/edit-comment.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +export class EditCommentDto { + @ApiProperty() + @IsString() + commentId: string; + + @ApiProperty() + @IsString() + newText: string; +} + diff --git a/ludos/backend/src/entities/comment.entity.ts b/ludos/backend/src/entities/comment.entity.ts index 60d9c001..e9ae7ca5 100644 --- a/ludos/backend/src/entities/comment.entity.ts +++ b/ludos/backend/src/entities/comment.entity.ts @@ -27,4 +27,7 @@ export class Comment { @Column() dislikes: number; + + @Column({default: false}) + edited: boolean; } diff --git a/ludos/backend/src/repositories/comment.repository.ts b/ludos/backend/src/repositories/comment.repository.ts index 41aff425..0c9802d8 100644 --- a/ludos/backend/src/repositories/comment.repository.ts +++ b/ludos/backend/src/repositories/comment.repository.ts @@ -29,4 +29,16 @@ export class CommentRepository extends Repository { comment.dislikes += 1; await this.save(comment); } + + public async deleteComment(commentId: string) { + let comment = await this.findCommentById(commentId); + await this.delete(comment); + } + + public async editComment(commentId: string, newText: string) { + let comment = await this.findCommentById(commentId); + comment.text = newText; + comment.edited = true; + await this.save(comment); + } } diff --git a/ludos/backend/src/services/comment.service.ts b/ludos/backend/src/services/comment.service.ts index 5702cf7e..4341d52f 100644 --- a/ludos/backend/src/services/comment.service.ts +++ b/ludos/backend/src/services/comment.service.ts @@ -6,6 +6,8 @@ import { import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; +import { DeleteCommentDto } from '../dtos/comment/request/delete-comment.dto'; +import { EditCommentDto } from '../dtos/comment/request/edit-comment.dto'; import { UserRepository } from '../repositories/user.repository'; import { CommentRepository } from '../repositories/comment.repository'; @@ -66,4 +68,30 @@ export class CommentService { await this.commentRepository.incrementDislikeCount(dislikeCommentDto.commentId); } + + public async deleteComment(userId: string, deleteCommentDto: DeleteCommentDto) { + let comment = await this.commentRepository.findCommentById(deleteCommentDto.commentId); + + if (!comment) { + throw new HttpException( + 'No comment found with this id', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.deleteComment(deleteCommentDto.commentId); + } + + public async editComment(userId: string, editCommentDto: EditCommentDto) { + let comment = await this.commentRepository.findCommentById(editCommentDto.commentId); + + if (!comment) { + throw new HttpException( + 'No comment found with this id', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.editComment(editCommentDto.commentId, editCommentDto.newText); + } } From 56cbe69554404bb13860b51b8dfba3041f0198a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= <68596831+Hakkush-07@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:00:38 +0300 Subject: [PATCH 5/6] remove unused imports --- ludos/backend/src/app.module.ts | 2 +- ludos/backend/src/controllers/user.controller.ts | 3 --- ludos/backend/src/services/user.service.ts | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/ludos/backend/src/app.module.ts b/ludos/backend/src/app.module.ts index afa3810a..670aba13 100644 --- a/ludos/backend/src/app.module.ts +++ b/ludos/backend/src/app.module.ts @@ -42,7 +42,7 @@ import { Review } from './entities/review.entity'; useClass: TypeOrmConfigService, inject: [TypeOrmConfigService], }), - TypeOrmModule.forFeature([User, Game, ResetPassword, Comment]), + TypeOrmModule.forFeature([User, Game, ResetPassword, Comment, Review]), ], controllers: [AppController, UserController, GameController, S3Controller, ReviewController, CommentController], providers: [ diff --git a/ludos/backend/src/controllers/user.controller.ts b/ludos/backend/src/controllers/user.controller.ts index d6793f27..dd75196f 100644 --- a/ludos/backend/src/controllers/user.controller.ts +++ b/ludos/backend/src/controllers/user.controller.ts @@ -27,9 +27,6 @@ import { VerifyCodeDto } from '../dtos/user/request/verify-code.dto'; import { ChangePasswordResponseDto } from '../dtos/user/response/change-password-response.dto'; import { ChangePasswordDto } from '../dtos/user/request/change-password.dto'; import { EditUserInfoDto } from '../dtos/user/request/edit-info.dto'; -import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; -import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; -import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; import { UserService } from '../services/user.service'; import { AuthGuard } from '../services/guards/auth.guard'; import { AuthorizedRequest } from '../interfaces/common/authorized-request.interface'; diff --git a/ludos/backend/src/services/user.service.ts b/ludos/backend/src/services/user.service.ts index 4ff7846a..6efed627 100644 --- a/ludos/backend/src/services/user.service.ts +++ b/ludos/backend/src/services/user.service.ts @@ -23,7 +23,6 @@ import { EditUserInfoDto } from '../dtos/user/request/edit-info.dto'; import { Payload } from '../interfaces/user/payload.interface'; import { ResetPasswordRepository } from '../repositories/reset-password.repository'; import { UserRepository } from '../repositories/user.repository'; -import { CommentRepository } from '../repositories/comment.repository'; import { GetUserInfoResponseDto } from '../dtos/user/response/get-user-info-response.dto'; @Injectable() From 507f9d1f4529a50b8cc120e012eac963a6dfdd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= <68596831+Hakkush-07@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:47:57 +0300 Subject: [PATCH 6/6] get comment endpoints --- .../src/controllers/comment.controller.ts | 78 +++++++-- .../comment/request/delete-comment.dto.ts | 8 - .../comment/request/dislike-comment.dto.ts | 8 - .../dtos/comment/request/edit-comment.dto.ts | 4 - .../dtos/comment/request/like-comment.dto.ts | 8 - .../response/get-comment.response.dto.ts | 32 ++++ ludos/backend/src/entities/comment.entity.ts | 19 ++- .../src/repositories/comment.repository.ts | 25 ++- ludos/backend/src/services/comment.service.ts | 154 ++++++++++++++++-- 9 files changed, 254 insertions(+), 82 deletions(-) delete mode 100644 ludos/backend/src/dtos/comment/request/delete-comment.dto.ts delete mode 100644 ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts delete mode 100644 ludos/backend/src/dtos/comment/request/like-comment.dto.ts create mode 100644 ludos/backend/src/dtos/comment/response/get-comment.response.dto.ts diff --git a/ludos/backend/src/controllers/comment.controller.ts b/ludos/backend/src/controllers/comment.controller.ts index 14c3f5fe..67dfe864 100644 --- a/ludos/backend/src/controllers/comment.controller.ts +++ b/ludos/backend/src/controllers/comment.controller.ts @@ -2,9 +2,13 @@ import { Body, Controller, HttpCode, + Get, Post, + Put, + Delete, Req, UseGuards, + Param, } from '@nestjs/common'; import { ApiBadRequestResponse, @@ -15,10 +19,8 @@ import { ApiUnauthorizedResponse, } from '@nestjs/swagger'; import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; -import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; -import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; -import { DeleteCommentDto } from '../dtos/comment/request/delete-comment.dto'; import { EditCommentDto } from '../dtos/comment/request/edit-comment.dto'; +import { GetCommentResponseDto } from '../dtos/comment/response/get-comment.response.dto'; import { CommentService } from '../services/comment.service'; import { AuthGuard } from '../services/guards/auth.guard'; import { AuthorizedRequest } from '../interfaces/common/authorized-request.interface'; @@ -28,7 +30,51 @@ import { AuthorizedRequest } from '../interfaces/common/authorized-request.inter export class CommentController { constructor(private readonly commentService: CommentService) { } - @ApiOperation({ summary: 'Comment on a post' }) + @ApiOperation({ summary: 'Get comment details' }) + @ApiOkResponse({ + description: 'Comment details', + type: GetCommentResponseDto, + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Get(':commentId/info') + public async getComment( + @Req() req: AuthorizedRequest, + @Param('commentId') commentId: string, + ) { + return await this.commentService.getComment(req.user.id, commentId); + } + + @ApiOperation({ summary: 'Get comments of post/comment/review' }) + @ApiOkResponse({ + description: 'Comments of post/comment/review', + type: [GetCommentResponseDto], + }) + @ApiUnauthorizedResponse({ + description: 'Invalid Credentials', + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + }) + @HttpCode(200) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @Get(':parentId') + public async getCommentDetails( + @Req() req: AuthorizedRequest, + @Param('parentId') parentId: string, + ) { + return await this.commentService.getCommentsByParent(req.user.id, parentId); + } + + @ApiOperation({ summary: 'Comment on a post/comment/review' }) @ApiOkResponse({ description: 'Comment', }) @@ -62,12 +108,12 @@ export class CommentController { @HttpCode(200) @ApiBearerAuth() @UseGuards(AuthGuard) - @Post('/like-comment') + @Post(':commentId/like-comment') public async likeComment( @Req() req: AuthorizedRequest, - @Body() input: LikeCommentDto, + @Param('commentId') commentId: string, ) { - await this.commentService.likeComment(req.user.id, input); + await this.commentService.likeComment(req.user.id, commentId); } @ApiOperation({ summary: 'Dislike a comment' }) @@ -83,12 +129,12 @@ export class CommentController { @HttpCode(200) @ApiBearerAuth() @UseGuards(AuthGuard) - @Post('/dislike-comment') + @Post(':commentId/dislike-comment') public async dislikeComment( @Req() req: AuthorizedRequest, - @Body() input: DislikeCommentDto, + @Param('commentId') commentId: string, ) { - await this.commentService.dislikeComment(req.user.id, input); + await this.commentService.dislikeComment(req.user.id, commentId); } @ApiOperation({ summary: 'Delete a comment' }) @@ -104,12 +150,13 @@ export class CommentController { @HttpCode(200) @ApiBearerAuth() @UseGuards(AuthGuard) - @Post('/delete-comment') + @Delete(':commentId/delete-comment') public async deleteComment( @Req() req: AuthorizedRequest, - @Body() input: DeleteCommentDto, + @Param('commentId') commentId: string, ) { - await this.commentService.deleteComment(req.user.id, input); + console.log("comment id: ", commentId); + await this.commentService.deleteComment(req.user.id, commentId); } @ApiOperation({ summary: 'Edit a comment' }) @@ -125,11 +172,12 @@ export class CommentController { @HttpCode(200) @ApiBearerAuth() @UseGuards(AuthGuard) - @Post('/edit-comment') + @Put(':commentId/edit-comment') public async editComment( @Req() req: AuthorizedRequest, + @Param('commentId') commentId: string, @Body() input: EditCommentDto, ) { - await this.commentService.editComment(req.user.id, input); + await this.commentService.editComment(req.user.id, commentId, input); } } diff --git a/ludos/backend/src/dtos/comment/request/delete-comment.dto.ts b/ludos/backend/src/dtos/comment/request/delete-comment.dto.ts deleted file mode 100644 index c9942957..00000000 --- a/ludos/backend/src/dtos/comment/request/delete-comment.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; -export class DeleteCommentDto { - @ApiProperty() - @IsString() - commentId: string; -} - diff --git a/ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts b/ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts deleted file mode 100644 index 6dcb5737..00000000 --- a/ludos/backend/src/dtos/comment/request/dislike-comment.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; -export class DislikeCommentDto { - @ApiProperty() - @IsString() - commentId: string; -} - diff --git a/ludos/backend/src/dtos/comment/request/edit-comment.dto.ts b/ludos/backend/src/dtos/comment/request/edit-comment.dto.ts index df1c4c69..25064bd7 100644 --- a/ludos/backend/src/dtos/comment/request/edit-comment.dto.ts +++ b/ludos/backend/src/dtos/comment/request/edit-comment.dto.ts @@ -1,10 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsString } from 'class-validator'; export class EditCommentDto { - @ApiProperty() - @IsString() - commentId: string; - @ApiProperty() @IsString() newText: string; diff --git a/ludos/backend/src/dtos/comment/request/like-comment.dto.ts b/ludos/backend/src/dtos/comment/request/like-comment.dto.ts deleted file mode 100644 index 24406661..00000000 --- a/ludos/backend/src/dtos/comment/request/like-comment.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; -export class LikeCommentDto { - @ApiProperty() - @IsString() - commentId: string; -} - diff --git a/ludos/backend/src/dtos/comment/response/get-comment.response.dto.ts b/ludos/backend/src/dtos/comment/response/get-comment.response.dto.ts new file mode 100644 index 00000000..0c2beac8 --- /dev/null +++ b/ludos/backend/src/dtos/comment/response/get-comment.response.dto.ts @@ -0,0 +1,32 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { User } from '../../../entities/user.entity'; +import { IsDate, IsBoolean, IsString, IsNumber } from 'class-validator'; + +export class GetCommentResponseDto { + @ApiProperty({type: () => User}) + author: User; + + @ApiProperty() + @IsDate() + timestamp: Date; + + @ApiProperty() + @IsString() + text: string; + + @ApiProperty() + @IsString() + parentId: string; + + @ApiProperty() + @IsBoolean() + edited: boolean; + + @ApiProperty() + @IsNumber() + likeCount: number; + + @ApiProperty() + @IsNumber() + dislikeCount: number; +} \ No newline at end of file diff --git a/ludos/backend/src/entities/comment.entity.ts b/ludos/backend/src/entities/comment.entity.ts index e9ae7ca5..ff7a8a6b 100644 --- a/ludos/backend/src/entities/comment.entity.ts +++ b/ludos/backend/src/entities/comment.entity.ts @@ -2,16 +2,19 @@ import { Entity, Column, PrimaryGeneratedColumn, + ManyToMany, + JoinTable, + ManyToOne, } from 'typeorm'; -//import { User } from '../entities/user.entity'; +import { User } from '../entities/user.entity'; @Entity('comments') export class Comment { @PrimaryGeneratedColumn('uuid') id: string; - @Column() - author: string; //User; + @ManyToOne(() => User, {eager: true}) + author: User; @Column() timestamp: Date; @@ -22,11 +25,13 @@ export class Comment { @Column() text: string; - @Column() - likes: number; + @ManyToMany('User', {eager: true}) + @JoinTable({ name: 'comment_user_likes' }) + likedUsers: User[] - @Column() - dislikes: number; + @ManyToMany('User', {eager: true}) + @JoinTable({ name: 'comment_user_dislikes' }) + dislikedUsers: User[]; @Column({default: false}) edited: boolean; diff --git a/ludos/backend/src/repositories/comment.repository.ts b/ludos/backend/src/repositories/comment.repository.ts index 0c9802d8..ffa7cad1 100644 --- a/ludos/backend/src/repositories/comment.repository.ts +++ b/ludos/backend/src/repositories/comment.repository.ts @@ -16,23 +16,13 @@ export class CommentRepository extends Repository { public findCommentById(id: string): Promise { return this.findOneBy({ id }); + //return this.findOne({ where: {id}, relations: {likedUsers: true, dislikedUsers: true} }); } - - public async incrementLikeCount(commentId: string) { - let comment = await this.findCommentById(commentId); - comment.likes += 1; - await this.save(comment); - } - - public async incrementDislikeCount(commentId: string) { - let comment = await this.findCommentById(commentId); - comment.dislikes += 1; - await this.save(comment); - } - + public async deleteComment(commentId: string) { - let comment = await this.findCommentById(commentId); - await this.delete(comment); + console.log("delete", commentId); + let x = await this.delete({id: commentId}); + console.log(x); } public async editComment(commentId: string, newText: string) { @@ -41,4 +31,9 @@ export class CommentRepository extends Repository { comment.edited = true; await this.save(comment); } + + public findCommentsByParent(parentId: string): Promise { + return this.findBy({ parentId }); + } + } diff --git a/ludos/backend/src/services/comment.service.ts b/ludos/backend/src/services/comment.service.ts index 4341d52f..6a2c4d3c 100644 --- a/ludos/backend/src/services/comment.service.ts +++ b/ludos/backend/src/services/comment.service.ts @@ -4,10 +4,8 @@ import { Injectable, } from '@nestjs/common'; import { WriteCommentDto } from '../dtos/comment/request/write-comment.dto'; -import { LikeCommentDto } from '../dtos/comment/request/like-comment.dto'; -import { DislikeCommentDto } from '../dtos/comment/request/dislike-comment.dto'; -import { DeleteCommentDto } from '../dtos/comment/request/delete-comment.dto'; import { EditCommentDto } from '../dtos/comment/request/edit-comment.dto'; +import { GetCommentResponseDto } from '../dtos/comment/response/get-comment.response.dto'; import { UserRepository } from '../repositories/user.repository'; import { CommentRepository } from '../repositories/comment.repository'; @@ -17,13 +15,61 @@ export class CommentService { private readonly userRepository: UserRepository, private readonly commentRepository: CommentRepository, ) {} + + public async getComment(userId: string, commentId: string): Promise { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this id', + HttpStatus.FORBIDDEN, + ); + } + + let comment = await this.commentRepository.findCommentById(commentId); + + if (!comment) { + throw new HttpException( + 'No comment found with this id', + HttpStatus.FORBIDDEN, + ); + } + return { + author: comment.author, + timestamp: comment.timestamp, + edited: comment.edited, + text: comment.text, + parentId: comment.parentId, + likeCount: comment.likedUsers.length, + dislikeCount: comment.dislikedUsers.length, + } + } + + public async getCommentsByParent(userId: string, parentId: string): Promise { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this id', + HttpStatus.FORBIDDEN, + ); + } + + return (await this.commentRepository.findCommentsByParent(parentId)).map((comment) => { + return { + likeCount: comment.likedUsers.length, + dislikeCount: comment.dislikedUsers.length, + ...comment, + } + }); + } public async writeComment(userId: string, writeCommentDto: WriteCommentDto) { let user = await this.userRepository.findUserById(userId); if (!user) { throw new HttpException( - 'No user found with this email', + 'No user found with this id', HttpStatus.FORBIDDEN, ); } @@ -33,18 +79,29 @@ export class CommentService { // parent id should be the identifier of one of the above let comment = { - author: userId, //user, + author: user, text: writeCommentDto.text, parentId: writeCommentDto.parentId, likes: 0, dislikes: 0, timestamp: new Date(), + likedUsers: [], + dislikedUsers: [], } await this.commentRepository.createComment(comment); } - public async likeComment(userId: string, likeCommentDto: LikeCommentDto) { - let comment = await this.commentRepository.findCommentById(likeCommentDto.commentId); + public async likeComment(userId: string, commentId: string) { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this id', + HttpStatus.FORBIDDEN, + ); + } + + let comment = await this.commentRepository.findCommentById(commentId); if (!comment) { throw new HttpException( @@ -53,11 +110,31 @@ export class CommentService { ); } - await this.commentRepository.incrementLikeCount(likeCommentDto.commentId); + if (comment.likedUsers.find(likedUser => likedUser.id === userId)) { + comment.likedUsers = comment.likedUsers.filter(likedUser => likedUser.id !== userId); + } + else if (comment.dislikedUsers.find(dislikedUser => dislikedUser.id === userId)) { + comment.dislikedUsers = comment.dislikedUsers.filter(dislikedUser => dislikedUser.id !== userId); + comment.likedUsers.push(user); + } + else { + comment.likedUsers.push(user); + } + + await this.commentRepository.save(comment); } - public async dislikeComment(userId: string, dislikeCommentDto: DislikeCommentDto) { - let comment = await this.commentRepository.findCommentById(dislikeCommentDto.commentId); + public async dislikeComment(userId: string, commentId: string) { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this id', + HttpStatus.FORBIDDEN, + ); + } + + let comment = await this.commentRepository.findCommentById(commentId); if (!comment) { throw new HttpException( @@ -66,11 +143,31 @@ export class CommentService { ); } - await this.commentRepository.incrementDislikeCount(dislikeCommentDto.commentId); + if (comment.dislikedUsers.find(dislikedUser => dislikedUser.id === userId)) { + comment.dislikedUsers = comment.dislikedUsers.filter(dislikedUser => dislikedUser.id !== userId); + } + else if (comment.likedUsers.find(likedUser => likedUser.id === userId)) { + comment.likedUsers = comment.likedUsers.filter(likedUser => likedUser.id !== userId); + comment.dislikedUsers.push(user); + } + else { + comment.dislikedUsers.push(user); + } + + await this.commentRepository.save(comment); } - public async deleteComment(userId: string, deleteCommentDto: DeleteCommentDto) { - let comment = await this.commentRepository.findCommentById(deleteCommentDto.commentId); + public async deleteComment(userId: string, commentId: string) { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this id', + HttpStatus.FORBIDDEN, + ); + } + + let comment = await this.commentRepository.findCommentById(commentId); if (!comment) { throw new HttpException( @@ -79,11 +176,27 @@ export class CommentService { ); } - await this.commentRepository.deleteComment(deleteCommentDto.commentId); + if (comment.author.id !== user.id) { + throw new HttpException( + 'User is not the author, can not delete', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.deleteComment(commentId); } - public async editComment(userId: string, editCommentDto: EditCommentDto) { - let comment = await this.commentRepository.findCommentById(editCommentDto.commentId); + public async editComment(userId: string, commentId: string, editCommentDto: EditCommentDto) { + let user = await this.userRepository.findUserById(userId); + + if (!user) { + throw new HttpException( + 'No user found with this id', + HttpStatus.FORBIDDEN, + ); + } + + let comment = await this.commentRepository.findCommentById(commentId); if (!comment) { throw new HttpException( @@ -92,6 +205,13 @@ export class CommentService { ); } - await this.commentRepository.editComment(editCommentDto.commentId, editCommentDto.newText); + if (comment.author.id !== user.id) { + throw new HttpException( + 'User is not the author, can not edit', + HttpStatus.FORBIDDEN, + ); + } + + await this.commentRepository.editComment(commentId, editCommentDto.newText); } }