Skip to content

Commit

Permalink
feat: reply comment crud
Browse files Browse the repository at this point in the history
  • Loading branch information
rrgks6221 committed Dec 14, 2023
1 parent ce70263 commit 637985a
Show file tree
Hide file tree
Showing 19 changed files with 774 additions and 9 deletions.
88 changes: 88 additions & 0 deletions src/apis/free-posts/controllers/free-posts.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ import { FreePostsItemDto } from '@src/apis/free-posts/dto/free-posts-item.dto';
import { PatchUpdateFreePostDto } from '@src/apis/free-posts/dto/patch-update-free-post.dto.td';
import { PutUpdateFreePostDto } from '@src/apis/free-posts/dto/put-update-free-post.dto';
import { CreateFreePostCommentDto } from '@src/apis/free-posts/free-post-comments/dto/create-free-post-comment.dto';
import { CreateFreePostReplyCommentDto } from '@src/apis/free-posts/free-post-comments/dto/create-free-post-reply-comment.dto';
import { FindFreePostCommentListQueryDto } from '@src/apis/free-posts/free-post-comments/dto/find-free-post-comment-list-query.dto';
import { FindFreePostReplyCommentListQueryDto } from '@src/apis/free-posts/free-post-comments/dto/find-free-post-reply-comment-list-query.dto';
import { FreePostCommentDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-comment.dto';
import { FreePostCommentsItemDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-comments-item.dto';
import { FreePostReplyCommentDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-reply-comment.dto';
import { FreePostReplyCommentsItemDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-reply-comments-item.dto';
import { PutUpdateFreePostCommentDto } from '@src/apis/free-posts/free-post-comments/dto/put-update-free-post-comment.dto';
import { PutUpdateFreePostReplyCommentDto } from '@src/apis/free-posts/free-post-comments/dto/put-update-free-post-reply-comment.dto';
import { UserDto } from '@src/apis/users/dto/user.dto';
import { User } from '@src/decorators/user.decorator';
import { ResponseType } from '@src/interceptors/success-interceptor/constants/success-interceptor.enum';
Expand Down Expand Up @@ -196,4 +201,87 @@ export class FreePostsController {
freePostCommentId,
);
}

@ApiFreePost.CreateReplyComment({ summary: '자유 게시글 대댓글 생성' })
@UseGuards(JwtAuthGuard)
@SetResponse({ key: 'freePostReplyComment', type: ResponseType.Detail })
@Post(':freePostId/comments/:freePostCommentId/reply')
createReplyComment(
@Param('freePostId', ParsePositiveIntPipe) freePostId: number,
@Param('freePostCommentId', ParsePositiveIntPipe) freePostCommentId: number,
@User() user: UserDto,
@Body() createFreePostReplyCommentDto: CreateFreePostReplyCommentDto,
): Promise<FreePostReplyCommentDto> {
return this.freePostsService.createReplyComment(
user.id,
freePostId,
freePostCommentId,
createFreePostReplyCommentDto,
);
}

@ApiFreePost.FindAllAndCountReplyComment({
summary: '자유 게시글 대댓글 전체조회(pagination)',
})
@SetResponse({ type: ResponseType.Pagination, key: 'freePostReplyComments' })
@Get(':freePostId/comments/:freePostCommentId')
async findAllAndCountReplyComment(
@Param('freePostId', ParsePositiveIntPipe) freePostId: number,
@Param('freePostCommentId', ParsePositiveIntPipe) freePostCommentId: number,
@Query()
findFreePostReplyCommentListQueryDto: FindFreePostReplyCommentListQueryDto,
): Promise<[FreePostReplyCommentsItemDto[], number]> {
const [freePosts, count] =
await this.freePostsService.findAllAndCountReplyComment(
freePostId,
freePostCommentId,
findFreePostReplyCommentListQueryDto,
);

return [plainToInstance(FreePostReplyCommentsItemDto, freePosts), count];
}

@ApiFreePost.PutUpdateReplyComment({ summary: '자유게시글 대댓글 수정' })
@SetResponse({ type: ResponseType.Detail, key: 'freePostReplyComment' })
@UseGuards(JwtAuthGuard)
@Put(':freePostId/comments/:freePostCommentId/reply/:freePostReplyCommentId')
putUpdateReplyComment(
@User() user: UserDto,
@Param('freePostId', ParsePositiveIntPipe) freePostId: number,
@Param('freePostCommentId', ParsePositiveIntPipe) freePostCommentId: number,
@Param('freePostReplyCommentId', ParsePositiveIntPipe)
freePostReplyCommentId: number,
@Body() putUpdateFreePostReplyCommentDto: PutUpdateFreePostReplyCommentDto,
): Promise<FreePostReplyCommentDto> {
return this.freePostsService.putUpdateReplyComment(
user.id,
freePostId,
freePostCommentId,
freePostReplyCommentId,
putUpdateFreePostReplyCommentDto,
);
}

@ApiFreePost.RemoveReplyComment({
summary: '자유게시글 대댓글 삭제',
})
@SetResponse({ type: ResponseType.Delete })
@UseGuards(JwtAuthGuard)
@Delete(
':freePostId/comments/:freePostCommentId/reply/:freePostReplyCommentId',
)
removeReplyComment(
@User() user: UserDto,
@Param('freePostId') freePostId: number,
@Param('freePostCommentId', ParsePositiveIntPipe) freePostCommentId: number,
@Param('freePostReplyCommentId', ParsePositiveIntPipe)
freePostReplyCommentId: number,
): Promise<number> {
return this.freePostsService.removeReplyComment(
user.id,
freePostId,
freePostCommentId,
freePostReplyCommentId,
);
}
}
144 changes: 144 additions & 0 deletions src/apis/free-posts/controllers/free-posts.swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { FreePostDto } from '@src/apis/free-posts/dto/free-post.dto';
import { FreePostsItemDto } from '@src/apis/free-posts/dto/free-posts-item.dto';
import { FreePostCommentDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-comment.dto';
import { FreePostCommentsItemDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-comments-item.dto';
import { FreePostReplyCommentDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-reply-comment.dto';
import { FreePostReplyCommentsItemDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-reply-comments-item.dto';
import { COMMON_ERROR_CODE } from '@src/constants/error/common/common-error-code.constant';
import { HttpException } from '@src/http-exceptions/exceptions/http.exception';
import { DeleteResponseDto } from '@src/interceptors/success-interceptor/dto/delete-response.dto';
Expand Down Expand Up @@ -383,4 +385,146 @@ export const ApiFreePost: ApiOperator<keyof FreePostsController> = {
]),
);
},

CreateReplyComment: (
apiOperationOptions: Required<Pick<Partial<OperationObject>, 'summary'>> &
Partial<OperationObject>,
): PropertyDecorator => {
return applyDecorators(
ApiOperation({
operationId: 'FreePostReplyCommentCreate',
...apiOperationOptions,
}),
ApiBearerAuth(),
DetailResponseDto.swaggerBuilder(
HttpStatus.CREATED,
'freePostReplyComment',
FreePostReplyCommentDto,
),
HttpException.swaggerBuilder(
HttpStatus.BAD_REQUEST,
[COMMON_ERROR_CODE.INVALID_REQUEST_PARAMETER],
{
description:
'해당 필드는 request parameter 가 잘못된 경우에만 리턴됩니다.',
type: ValidationError,
},
),
HttpException.swaggerBuilder(HttpStatus.UNAUTHORIZED, [
COMMON_ERROR_CODE.INVALID_TOKEN,
]),
HttpException.swaggerBuilder(HttpStatus.NOT_FOUND, [
COMMON_ERROR_CODE.RESOURCE_NOT_FOUND,
]),
HttpException.swaggerBuilder(HttpStatus.INTERNAL_SERVER_ERROR, [
COMMON_ERROR_CODE.SERVER_ERROR,
]),
);
},

FindAllAndCountReplyComment: (
apiOperationOptions: Required<Pick<Partial<OperationObject>, 'summary'>> &
Partial<OperationObject>,
): PropertyDecorator => {
return applyDecorators(
ApiOperation({
operationId: 'FreePostReplyCommentFindAllAndCount',
...apiOperationOptions,
}),
PaginationResponseDto.swaggerBuilder(
HttpStatus.OK,
'freePostReplyComments',
FreePostReplyCommentsItemDto,
),
HttpException.swaggerBuilder(
HttpStatus.BAD_REQUEST,
[COMMON_ERROR_CODE.INVALID_REQUEST_PARAMETER],
{
description:
'해당 필드는 request parameter 가 잘못된 경우에만 리턴됩니다.',
type: ValidationError,
},
),
HttpException.swaggerBuilder(HttpStatus.INTERNAL_SERVER_ERROR, [
COMMON_ERROR_CODE.SERVER_ERROR,
]),
HttpException.swaggerBuilder(HttpStatus.NOT_FOUND, [
COMMON_ERROR_CODE.RESOURCE_NOT_FOUND,
]),
);
},

PutUpdateReplyComment: (
apiOperationOptions: Required<Pick<Partial<OperationObject>, 'summary'>> &
Partial<OperationObject>,
): PropertyDecorator => {
return applyDecorators(
ApiOperation({
operationId: 'FreePostReplyCommentPutUpdate',
...apiOperationOptions,
}),
ApiBearerAuth(),
DetailResponseDto.swaggerBuilder(
HttpStatus.OK,
'freePostReplyComment',
FreePostReplyCommentDto,
),
HttpException.swaggerBuilder(
HttpStatus.BAD_REQUEST,
[COMMON_ERROR_CODE.INVALID_REQUEST_PARAMETER],
{
description:
'해당 필드는 request parameter 가 잘못된 경우에만 리턴됩니다.',
type: ValidationError,
},
),
HttpException.swaggerBuilder(HttpStatus.UNAUTHORIZED, [
COMMON_ERROR_CODE.INVALID_TOKEN,
]),
HttpException.swaggerBuilder(HttpStatus.FORBIDDEN, [
COMMON_ERROR_CODE.PERMISSION_DENIED,
]),
HttpException.swaggerBuilder(HttpStatus.NOT_FOUND, [
COMMON_ERROR_CODE.RESOURCE_NOT_FOUND,
]),
HttpException.swaggerBuilder(HttpStatus.INTERNAL_SERVER_ERROR, [
COMMON_ERROR_CODE.SERVER_ERROR,
]),
);
},

RemoveReplyComment: (
apiOperationOptions: Required<Pick<Partial<OperationObject>, 'summary'>> &
Partial<OperationObject>,
): PropertyDecorator => {
return applyDecorators(
ApiOperation({
operationId: 'FreePostReplyCommentRemove',
...apiOperationOptions,
}),
ApiBearerAuth(),
DeleteResponseDto.swaggerBuilder(HttpStatus.OK, 'freePost'),
HttpException.swaggerBuilder(
HttpStatus.BAD_REQUEST,
[COMMON_ERROR_CODE.INVALID_REQUEST_PARAMETER],
{
description:
'해당 필드는 request parameter 가 잘못된 경우에만 리턴됩니다.',
type: ValidationError,
},
),
HttpException.swaggerBuilder(HttpStatus.UNAUTHORIZED, [
COMMON_ERROR_CODE.INVALID_TOKEN,
]),
HttpException.swaggerBuilder(HttpStatus.FORBIDDEN, [
COMMON_ERROR_CODE.PERMISSION_DENIED,
]),
HttpException.swaggerBuilder(HttpStatus.NOT_FOUND, [
COMMON_ERROR_CODE.RESOURCE_NOT_FOUND,
]),
HttpException.swaggerBuilder(HttpStatus.INTERNAL_SERVER_ERROR, [
COMMON_ERROR_CODE.SERVER_ERROR,
]),
);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ export const FREE_POST_COMMENT_DESCRIPTION_LENGTH = {
MIN: 1,
MAX: 255,
} as const;

export const FREE_POST_REPLY_COMMENT_DESCRIPTION_LENGTH = {
MIN: 1,
MAX: 255,
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ export enum FreePostCommentStatus {
Posting = 'posting',
Remove = 'remove',
}

export enum FreePostReplyCommentStatus {
Posting = 'posting',
Remove = 'remove',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { FREE_POST_REPLY_COMMENT_DESCRIPTION_LENGTH } from '@src/apis/free-posts/free-post-comments/constants/free-post-comment.constant';
import { FreePostReplyComment } from '@src/entities/FreePostReplyComment';
import { IsBoolean, IsOptional, Length } from 'class-validator';

export class CreateFreePostReplyCommentDto
implements Pick<FreePostReplyComment, 'description' | 'isAnonymous'>
{
@ApiProperty({
description: '본문',
minLength: FREE_POST_REPLY_COMMENT_DESCRIPTION_LENGTH.MIN,
maxLength: FREE_POST_REPLY_COMMENT_DESCRIPTION_LENGTH.MAX,
})
@Length(
FREE_POST_REPLY_COMMENT_DESCRIPTION_LENGTH.MIN,
FREE_POST_REPLY_COMMENT_DESCRIPTION_LENGTH.MAX,
)
description: string;

@ApiPropertyOptional({
description: '익명 여부',
default: false,
})
@IsOptional()
@IsBoolean()
isAnonymous: boolean = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { FREE_POST_ORDER_FIELD } from '@src/apis/free-posts/constants/free-post.constant';
import { FreePostReplyCommentStatus } from '@src/apis/free-posts/free-post-comments/constants/free-post-comment.enum';
import { FreePostReplyCommentDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-reply-comment.dto';
import { SortOrder } from '@src/constants/enum';
import { PageDto } from '@src/dto/page.dto';
import { ApiPropertyOrder } from '@src/dto/swagger/api-property-order.decorator';
import { CsvToOrder, Order } from '@src/dto/transformer/csv-to-order.decorator';
import { IsDefined, IsOptional } from 'class-validator';

export class FindFreePostReplyCommentListQueryDto
extends PageDto
implements Partial<FreePostReplyCommentDto>
{
@ApiPropertyOrder(FREE_POST_ORDER_FIELD)
@CsvToOrder<typeof FREE_POST_ORDER_FIELD>([...FREE_POST_ORDER_FIELD])
@IsOptional()
order: Order<typeof FREE_POST_ORDER_FIELD> = { id: SortOrder.Desc };

@IsDefined()
status: FreePostReplyCommentStatus = FreePostReplyCommentStatus.Posting;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ApiProperty } from '@nestjs/swagger';
import { FREE_POST_COMMENT_DESCRIPTION_LENGTH } from '@src/apis/free-posts/free-post-comments/constants/free-post-comment.constant';
import { FreePostReplyCommentStatus } from '@src/apis/free-posts/free-post-comments/constants/free-post-comment.enum';
import { BaseDto } from '@src/dto/base.dto';
import { FreePostReplyComment } from '@src/entities/FreePostReplyComment';
import { Exclude } from 'class-transformer';

export class FreePostReplyCommentDto
extends BaseDto
implements
Pick<
FreePostReplyComment,
| 'id'
| 'userId'
| 'freePostCommentId'
| 'description'
| 'isAnonymous'
| 'status'
| 'createdAt'
| 'updatedAt'
| 'deletedAt'
>
{
@ApiProperty({
description: '게시글 댓글 고유 ID',
format: 'integer',
})
freePostCommentId: number;

@ApiProperty({
description: '대댓글 작성자 고유 ID',
format: 'integer',
})
userId: number;

@ApiProperty({
description: '본문',
minLength: FREE_POST_COMMENT_DESCRIPTION_LENGTH.MIN,
maxLength: FREE_POST_COMMENT_DESCRIPTION_LENGTH.MAX,
})
description: string;

@ApiProperty({
description: '익명 여부',
})
isAnonymous: boolean;

@Exclude()
status: FreePostReplyCommentStatus;

@Exclude()
deletedAt: Date;

constructor(freePostDto: Partial<FreePostReplyCommentDto> = {}) {
super();

Object.assign(this, freePostDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { FreePostReplyCommentDto } from '@src/apis/free-posts/free-post-comments/dto/free-post-reply-comment.dto';

export class FreePostReplyCommentsItemDto extends FreePostReplyCommentDto {}
Loading

0 comments on commit 637985a

Please sign in to comment.