Skip to content

Commit

Permalink
[v0.1.3] n번째 티켓(랜딩페이지용), 댓글 조회기능 수정 배포
Browse files Browse the repository at this point in the history
[v0.1.3] n번째 티켓(랜딩페이지용), 댓글 조회기능 수정 배포
  • Loading branch information
ImNM authored Jul 31, 2022
2 parents 9cd670d + f18993c commit 78b685b
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 75 deletions.
5 changes: 5 additions & 0 deletions src/auth/guards/AccessToken.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ export class AccessTokenGuard implements CanActivate {
canActivate(
context: ExecutionContext
): boolean | Promise<boolean> | Observable<boolean> {
//@NoAuth 사용시 해당 부분에서 AccessTokenGuard 사용 해제시킴
const noAuth = this.reflector.get<boolean>('no-auth', context.getHandler())
if (noAuth) {
return true;
}
const request = context.switchToHttp().getRequest();
return this.validateRequest(request, context);
}
Expand Down
5 changes: 5 additions & 0 deletions src/auth/guards/NoAuth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SetMetadata } from '@nestjs/common'

/** AccessTokenGuard 가 포함된 controller 내부에서
* AccessToken 없이 접근할 수 있도록 해주는 데코레이터입니다 */
export const NoAuth = () => SetMetadata('no-auth', true)
58 changes: 38 additions & 20 deletions src/database/repositories/comment.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,54 +19,72 @@ export class CommentRepository {
@InjectRepository(Comment)
private commentRepository: Repository<Comment>
) {}

// 응원 댓글 생성
async makeComment(user: User, requestCommentDto: RequestCommentDto) {
const { content, nickName } = requestCommentDto;
const ret_user = plainToInstance(UserProfileDto, user);
const comment = this.commentRepository.create({
nickName,
content,
user: ret_user,
})
user: ret_user
});

await this.commentRepository.save(comment);

const ret_comment = {
...comment,
iUserId: ret_user.id
}
};
return plainToInstance(ResponseCommentDto, ret_comment);
}

// 응원 댓글 조회
async getAllComment(userId: number, scrollOptionsDto: ScrollOptionsDto) {
const queryBuilder = await this.commentRepository.createQueryBuilder('comment');

const { lastId } = scrollOptionsDto;
const queryBuilder = this.commentRepository.createQueryBuilder('comment');
// 한 번에 조회하는 댓글 수
const take = 20;
queryBuilder
.leftJoinAndSelect('comment.user', 'user')
.orderBy('comment.createdAt', "DESC")
.skip(scrollOptionsDto.skip)
.take(scrollOptionsDto.take);

const itemCount = await queryBuilder.getCount();
.orderBy('comment.createdAt', 'DESC')
.limit(take);

if (lastId) {
queryBuilder.where('comment.id < :lastId', { lastId: lastId });
}

const { entities } = await queryBuilder.getRawAndEntities();
console.log(entities);
const lastId = entities[entities.length - 1].id;
const scrollMetaDto = new ScrollMetaDto(scrollOptionsDto, itemCount, lastId);

// ScrollMetaDto의 인자 값 : checkLastId, lastPage
let checkLastId: null | number;
let lastPage = false;
// lastId값 초기화
if (entities.length) {
checkLastId = entities[entities.length - 1].id;
} else {
checkLastId = null;
}
// 마지막 페이지인지 확인
if (entities.length < take) {
lastPage = true;
} else {
lastPage = false;
}

const scrollMetaDto = new ScrollMetaDto(checkLastId, lastPage);

return new ResponseScrollCommentDto(entities, scrollMetaDto);
}

// 댓글 삭제
async deleteComment(id: number) {
const comment = await this.commentRepository.findOne({ where: {id: id}})
const comment = await this.commentRepository.findOne({ where: { id: id } });
const result = await this.commentRepository.delete(id);

// 해당 아이디가 존재하는지 확인 후 없으면 오류 메시지 출력
if (result.affected === 0) {
throw new NotFoundException(`해당 id ${id}를 찾을 수 없습니다.`);
}
if (result.affected === 0) {
throw new NotFoundException(`해당 id ${id}를 찾을 수 없습니다.`);
}
return plainToInstance(CommentDto, comment);
}
}
}
6 changes: 6 additions & 0 deletions src/database/repositories/ticket.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ export class TicketRepository {
.getMany();
}


/** DB에 저장된 티켓의 개수를 반환한다 */
async countTicket(): Promise<number> {
return await this.ticketRepository.count();
}

/**
* 해당 티켓을 저장한다
* @param ticket 저장할 티켓
Expand Down
13 changes: 13 additions & 0 deletions src/tickets/dtos/ticket-count.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';
import { IsNumber } from 'class-validator';

/** /tickets/count에 대한 Swagger 응답을 위한 dto 입니다 */
export class TicketCountDto {
@ApiProperty({
description: 'DB에 저장된 티켓의 총 개수',
})
@IsNumber()
@Expose()
readonly count: number;
}
19 changes: 18 additions & 1 deletion src/tickets/tickets.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { TicketFindDto } from './dtos/ticket-find.dto';
import { UpdateTicketStatusDto } from './dtos/update-ticket-status.dto';
import { SuccessResponse } from 'src/common/decorators/SuccessResponse.decorator';
import { PageDto } from 'src/common/dtos/page/page.dto';
import { NoAuth } from 'src/auth/guards/NoAuth.guard';
import { TicketCountDto } from './dtos/ticket-count.dto';

@ApiTags('tickets')
@ApiBearerAuth('accessToken')
Expand Down Expand Up @@ -141,6 +143,21 @@ export class TicketsController {
return this.ticketService.createTicket(createTicketDto);
}

@ApiOperation({
summary: '[랜딩페이지] 티켓 개수를 반환한다'
})
@ApiResponse({
status: 200,
description: '요청 성공시',
type: TicketCountDto
})
@NoAuth()
@Get('/count')
async getTicketCount() {
const count = await this.ticketService.countTicket();
return { count: count };
}

@ApiOperation({
summary: '해당 uuid를 포함하는 티켓을 가져온다, req.user 필요'
})
Expand Down Expand Up @@ -221,7 +238,7 @@ export class TicketsController {
description: '어드민이 아닐 경우'
})
@Roles(Role.Admin)
@Delete('/delete/:uuid')
@Delete('/:uuid/delete')
deleteTicketByUuid(@Param('uuid') ticketUuid: string) {
return this.ticketService.deleteTicketByUuid(ticketUuid);
}
Expand Down
6 changes: 5 additions & 1 deletion src/tickets/tickets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class TicketsService {

//어드민이거나 Ticket.user.id === user.id 일때만 리턴
if (ticket.user.id !== user.id && user.role !== Role.Admin) {
throw new UnauthorizedException('해등 티켓에 대한 접근 권한이 없습니다');
throw new UnauthorizedException('해당 티켓에 대한 접근 권한이 없습니다');
}

return ticket;
Expand Down Expand Up @@ -88,6 +88,10 @@ export class TicketsService {
);
}

async countTicket(): Promise<number> {
return await this.ticketRepository.countTicket();
}

/**
* 어드민이 티켓을 찍었을때 연결할 url에서 검증을 완료한 후 소켓 메세지 전송
* @param uuid TicketValidationDto -> uuid
Expand Down
28 changes: 4 additions & 24 deletions src/users/dtos/Scroll/ScrollMeta.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,16 @@ import { Expose } from 'class-transformer';
import { ScrollOptionsDto } from './ScrollOptions.dto';

export class ScrollMetaDto {
@ApiProperty({ description: '페이지 정보입니다.' })
@Expose()
readonly page: number;

@ApiProperty({ description: '몇개를 받아가는지 한페이지 당 원소갯수' })
@Expose()
readonly take: number;

@ApiProperty({ description: '총 아이템 숫자' })
@Expose()
readonly itemCount: number;

@ApiProperty({ description: '총 페이지 숫자' })
@Expose()
readonly pageCount: number;

@ApiProperty({ description: '현재 페이지의 마지막 id', nullable: true })
@Expose()
readonly lastId: number;
readonly lastId: number | null;

@ApiProperty({ description: '현재 페이지가 마지막페이지인지에 대한 정보'})
@ApiProperty({ description: '현재 페이지가 마지막페이지인지에 대한 정보' })
@Expose()
readonly lastPage: boolean;

constructor(scrollOptionsDto: ScrollOptionsDto, itemCount: number, lastId: number) {
this.page = scrollOptionsDto.page;
this.take = scrollOptionsDto.take;
this.itemCount = itemCount;
this.pageCount = Math.ceil(this.itemCount / this.take);
constructor(lastId: number | null, lastPage: boolean) {
this.lastId = lastId;
this.lastPage = this.page >= this.pageCount;
this.lastPage = lastPage;
}
}
31 changes: 5 additions & 26 deletions src/users/dtos/Scroll/ScrollOptions.dto.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,12 @@
import { ApiPropertyOptional } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Expose, Type } from 'class-transformer';
import { IsInt, IsOptional, Max, Min } from 'class-validator';

export class ScrollOptionsDto {
@ApiPropertyOptional({
minimum: 1,
default: 1
description: '현재 페이지의 마지막 id',
nullable: true
})
@Type(() => Number)
@IsInt()
@Min(1)
@IsOptional()
@Expose()
readonly page: number = 1;

@ApiPropertyOptional({
minimum: 1,
maximum: 50,
default: 20
})
@Type(() => Number)
@IsInt()
@Min(1)
@Max(50)
@IsOptional()
@Expose()
readonly take: number = 10;

get skip(): number {
return (this.page - 1) * this.take;
}
}
readonly lastId: number;
}
16 changes: 13 additions & 3 deletions src/users/users.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MockType } from 'src/common/funcs/mockType';
import { CommentRepository } from 'src/database/repositories/comment.repository';
import { UserRepository } from 'src/database/repositories/user.repository';
import { UsersService } from './users.service';

export const repositoryMockFactory: () => MockType<UserRepository> = jest.fn(
const UserRepositoryMockFactory: () => MockType<UserRepository> = jest.fn(
() => ({
findAll: jest.fn(entity => entity)
// ...
})
);
const CommentRepositoryMockFactory: () => MockType<UserRepository> = jest.fn(
() => ({
findAll: jest.fn(entity => entity)
// ...
})
);

describe('UsersService', () => {
let service: UsersService;

Expand All @@ -19,7 +25,11 @@ describe('UsersService', () => {
UsersService,
{
provide: UserRepository,
useFactory: repositoryMockFactory
useFactory: UserRepositoryMockFactory
},
{
provide: CommentRepository,
useFactory: CommentRepositoryMockFactory
}
]
}).compile();
Expand Down

0 comments on commit 78b685b

Please sign in to comment.