Skip to content

Commit

Permalink
Merge pull request #98 from modern-agile-team/feature/comment
Browse files Browse the repository at this point in the history
ADD(2swo/Comment) 댓글 기능 추가
  • Loading branch information
2swo authored Nov 7, 2023
2 parents a36c504 + 9ab6c9c commit ddb124a
Show file tree
Hide file tree
Showing 10 changed files with 623 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { SearchModule } from './search/search.module';
UserModule,
TypeOrmModule.forRoot({
...TypeORMconfig, // TypeORM 설정 객체 확장
synchronize: false, // DB 동기화 여부 설정
synchronize: true, // DB 동기화 여부 설정
}),
// TypeOrmModule.forFeature([Image]),
ConfigModule.forRoot({
Expand Down
41 changes: 38 additions & 3 deletions src/comments/controllers/comments.controller.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import {
Controller,
// Get,
Post,
Body,
// Patch,
// Delete,
Headers,
Query,
Get,
Patch,
Delete,
} from '@nestjs/common';
import { Comment } from '../entities/comment.entity';
import { ApiTags } from '@nestjs/swagger';
import { CommentsService } from '../services/comments.services';
import { TokenService } from 'src/auth/services/token.service';
import { CreateCommentDto } from '../dto/create-comment-dto';
import { ApiAddComment } from '../swagger-decoratros/add-comment-decorators';
import { ApiGetAllComment } from '../swagger-decoratros/get-all-comment-decoratros';
import { commentResponseDTO } from '../dto/get-all-comment-dto';
import { ApiUpdateComment } from '../swagger-decoratros/patch-comment-decoratros';
import { ApiDeleteComment } from '../swagger-decoratros/delete-comment-decorator';

@Controller('comments')
@ApiTags('Comment API')
Expand All @@ -23,6 +28,7 @@ export class CommentsController {
) {}

@Post('')
@ApiAddComment()
async createComment(
@Headers('access_token') accessToken: string,
@Query('boardId') boardId: number,
Expand All @@ -31,4 +37,33 @@ export class CommentsController {
const userId = await this.tokenService.decodeToken(accessToken);
return await this.commentsService.create(createCommentDto, userId, boardId);
}

@Get('')
@ApiGetAllComment()
async getComment(
@Headers('access_token') accessToken: string,
@Query('boardId') boardId: number,
): Promise<commentResponseDTO[]> {
const userId = await this.tokenService.decodeToken(accessToken);
return this.commentsService.findAllComments(boardId, userId);
}

@Patch('')
@ApiUpdateComment()
async updateComment(
@Query('commentId') commentId: number,
@Body() commentData: Partial<Comment>,
): Promise<Comment> {
return this.commentsService.updateComment(commentId, commentData);
}

@Delete('')
@ApiDeleteComment()
async deleteComment(
@Query('commentId') commentId: number,
@Headers('access_token') accessToken: string,
) {
const userId = await this.tokenService.decodeToken(accessToken);
await this.commentsService.deleteComment(commentId, userId);
}
}
11 changes: 11 additions & 0 deletions src/comments/dto/get-all-comment-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { UserImage } from 'src/users/entities/user-image.entity';

export class commentResponseDTO {
id: number;
content: string;
commentowner: true | false;
userId: {
name: string;
userImage: UserImage | UserImage[];
};
}
6 changes: 6 additions & 0 deletions src/comments/dto/update-comment-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IsString } from 'class-validator';

export class UpdateCommentDto {
@IsString()
content: string;
}
38 changes: 38 additions & 0 deletions src/comments/repository/comments.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,42 @@ export class CommentsRepository {
comment.boardId = boardId;
return await this.entityManager.save(Comment, comment);
}

async findCommentsByBoardId(boardId: number): Promise<Comment[]> {
const query = this.entityManager
.createQueryBuilder(Comment, 'comment')
.innerJoinAndSelect('comment.user', 'user')
.innerJoinAndSelect('user.userImage', 'userImage')
.where('comment.boardId = :boardId', { boardId });

return query.getMany();
}

async findOneComment(id: number): Promise<Comment> {
return await this.entityManager.findOne(Comment, {
relations: ['user', 'user.userImage'],
where: { id },
});
}

async updateComment(
id: number,
commentData: Partial<CreateCommentDto>,
): Promise<Comment> {
const existingComment = await this.entityManager.findOne(Comment, {
relations: ['user', 'user.userImage'],
where: { id },
});
for (const key in commentData) {
if (commentData.hasOwnProperty(key)) {
existingComment[key] = commentData[key];
}
}
await this.entityManager.save(Comment, existingComment);
return existingComment;
}

async deleteComment(comment: Comment): Promise<void> {
await this.entityManager.remove(Comment, comment);
}
}
53 changes: 53 additions & 0 deletions src/comments/services/comments.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common';
import { CommentsRepository } from '../repository/comments.repository';
import { CreateCommentDto } from '../dto/create-comment-dto';
import { Comment } from '../entities/comment.entity';
import { commentResponseDTO } from '../dto/get-all-comment-dto';
import { UpdateCommentDto } from '../dto/update-comment-dto';

@Injectable()
export class CommentsService {
Expand All @@ -21,4 +23,55 @@ export class CommentsService {
console.log(error);
}
}

async findAllComments(
boardId: number,
userId: number,
): Promise<commentResponseDTO[]> {
const comments =
await this.CommentRepository.findCommentsByBoardId(boardId);
if (!comments) {
throw new Error('댓글을 찾을 수 없습니다.');
}
return comments.map((comment) => ({
id: comment.id,
content: comment.content,
commentowner: comment.userId === userId,
userId: {
name: comment.user.name,
userImage: comment.user.userImage ? comment.user.userImage : [],
},
}));
}

async updateComment(
commentId: number,
commentData: Partial<UpdateCommentDto>,
): Promise<Comment | undefined> {
const existingComment =
await this.CommentRepository.findOneComment(commentId);
for (const key in commentData) {
if (commentData.hasOwnProperty(key)) {
existingComment[key] = commentData[key];
}
}
const updatedComment = await this.CommentRepository.updateComment(
commentId,
existingComment,
);
return updatedComment;
}

async deleteComment(commentId: number, userId: number): Promise<void> {
const comment = await this.CommentRepository.findOneComment(commentId);

if (!comment) {
throw new Error('존재하지 않는 댓글입니다.');
}

if (comment.userId !== userId) {
throw new Error('작성한 댓글이 아닙니다.');
}
await this.CommentRepository.deleteComment(comment);
}
}
103 changes: 103 additions & 0 deletions src/comments/swagger-decoratros/add-comment-decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { applyDecorators } from '@nestjs/common';
import {
ApiBody,
ApiHeaders,
ApiOperation,
ApiParam,
ApiResponse,
} from '@nestjs/swagger';

export function ApiAddComment() {
return applyDecorators(
ApiOperation({
summary: '댓글을 생성하는 API',
description: '댓글을 생성하는 API',
}),
ApiResponse({
status: 200,
description: '성공적으로 댓글을 생성한 경우',
content: {
JSON: {
example: {
content: '댓글 1차시도',
userId: '작성한 userId가 넘어옵니다',
boardId: '작성한 보드의 boardId가 넘어옵니다',
id: '댓글 고유 id',
createAt: '작성한 시간이 넘어옵니다',
},
},
},
}),
ApiResponse({
status: 401,
description: '우리 서비스의 액세스 토큰이 아닌 경우',
content: {
JSON: {
example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' },
},
},
}),
ApiResponse({
status: 403,
description: '만료된 액세스 토큰인 경우',
content: {
JSON: {
example: { statusCode: 403, message: '만료된 토큰입니다.' },
},
},
}),
ApiResponse({
status: 404,
description: 'DB에서 보드를 찾을 수 없는 경우',
content: {
JSON: {
example: { statusCode: 404, message: '보드를 찾을 수 없습니다.' },
},
},
}),
ApiResponse({
status: 411,
description: '액세스 토큰이 제공되지 않은 경우',
content: {
JSON: {
example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' },
},
},
}),
ApiResponse({
status: 500,
description: '댓글을 생성하는 중 오류가 발생한 경우',
content: {
JSON: {
example: {
statusCode: 500,
message: '댓글을 생성하는 중 오류가 발생했습니다.',
},
},
},
}),
ApiHeaders([
{
name: 'access_token',
description: '액세스 토큰',
required: true,
example: '여기에 액세스 토큰',
},
]),
ApiParam({
name: 'boardId',
description: '댓글을 추가할 보드의 ID',
}),
ApiBody({
schema: {
type: 'object',
properties: {
content: { type: 'string' },
},
example: {
content: '추가할 댓글 입력입니다.',
},
},
}),
);
}
85 changes: 85 additions & 0 deletions src/comments/swagger-decoratros/delete-comment-decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { applyDecorators } from '@nestjs/common';
import {
ApiHeaders,
ApiOperation,
ApiParam,
ApiResponse,
} from '@nestjs/swagger';

export function ApiDeleteComment() {
return applyDecorators(
ApiOperation({
summary: '댓글을 삭제하는 API',
description: '댓글을 삭제하는 API',
}),
ApiResponse({
status: 200,
description: '성공적으로 댓글을 삭제한 경우',
content: {
JSON: {
example: {},
},
},
}),
ApiResponse({
status: 401,
description: '우리 서비스의 액세스 토큰이 아닌 경우',
content: {
JSON: {
example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' },
},
},
}),
ApiResponse({
status: 403,
description: '만료된 액세스 토큰인 경우',
content: {
JSON: {
example: { statusCode: 403, message: '만료된 토큰입니다.' },
},
},
}),
ApiResponse({
status: 404,
description: 'DB에서 댓글을 찾을 수 없는 경우',
content: {
JSON: {
example: { statusCode: 404, message: '댓글을 찾을 수 없습니다.' },
},
},
}),
ApiResponse({
status: 411,
description: '액세스 토큰이 제공되지 않은 경우',
content: {
JSON: {
example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' },
},
},
}),
ApiResponse({
status: 500,
description: '댓글을 삭제하는 오류가 발생한 경우',
content: {
JSON: {
example: {
statusCode: 500,
message: '댓글을 삭제하는 중 오류가 발생했습니다.',
},
},
},
}),
ApiHeaders([
{
name: 'access_token',
description: '액세스 토큰',
required: true,
example: '여기에 액세스 토큰',
},
]),
ApiParam({
name: 'commentId',
description: '삭제할 댓글의 ID',
}),
);
}
Loading

0 comments on commit ddb124a

Please sign in to comment.