Skip to content

Commit

Permalink
[BE#505] 팔로우 조회 커서기반 pagination (#506)
Browse files Browse the repository at this point in the history
* fix: is_blocked 추가 안되었음

* refactor: auth, user의 swagger 반영

* feat: 커서기반 페이지네이션 구현

- followers 쿼리에는 is_followed 추가
- /mates/followers?cursor=1 요청을 통해 페이지네이션 구현
- query를 string으로 받는데, 변환 과정을 dto에서 하고자 main.ts에 과련 설정 추가

* refactor: cursor이름 page로 변경

* fix: 친구관계가 아닐 때에도 반영

* fix: 친구관계가 아닐 때에 에러

* chore: follow api 관련 swagger 문서 변경
  • Loading branch information
yeongbinim authored Jan 23, 2024
1 parent 6cb19bb commit fea02e8
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 35 deletions.
17 changes: 12 additions & 5 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import { User } from 'src/users/decorator/user.decorator';
import {
ApiBearerAuth,
ApiConsumes,
ApiCreatedResponse,
ApiExcludeEndpoint,
ApiOkResponse,
ApiOperation,
ApiResponse,
ApiTags,
Expand All @@ -32,6 +34,8 @@ import { ConfigService } from '@nestjs/config';
import { ENV } from 'src/common/const/env-keys.const';
import { getImageUrl } from 'src/common/utils/utils';
import { ResponseDto } from 'src/common/response.dto';
import { UpdateInfoDto } from './dto/response/update-info.dto';
import { GetInfoDto } from './dto/response/get-info.dto';

@ApiTags('로그인 페이지')
@Controller('auth')
Expand Down Expand Up @@ -81,15 +85,18 @@ export class AuthController {
@UseInterceptors(FileInterceptor('image'))
@ApiOperation({ summary: '유저 정보 설정 (완)' })
@ApiConsumes('multipart/form-data')
@ApiResponse({ status: 200, description: '프로필 변경 성공' })
@ApiCreatedResponse({
type: UpdateInfoDto,
description: '유저 정보가 성공적으로 업데이트 되었습니다.',
})
@ApiResponse({ status: 400, description: '잘못된 요청' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiBearerAuth()
async patchUser(
@User('id') user_id: number,
@Body() user: UpdateUserDto,
@UploadedFile() file: Express.Multer.File,
): Promise<any> {
): Promise<UpdateInfoDto> {
let image_url: string;
if (file) {
const isNomal = await this.usersService.isNormalImage(file);
Expand Down Expand Up @@ -120,11 +127,11 @@ export class AuthController {
@Get('info')
@UseGuards(AccessTokenGuard)
@ApiOperation({ summary: '유저 정보 조회 (완)' })
@ApiResponse({ status: 200, description: '프로필 조회 성공' })
@ApiOkResponse({ type: GetInfoDto, description: '프로필 조회 성공' })
@ApiResponse({ status: 400, description: '잘못된 요청' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiBearerAuth()
async getUser(@User('id') user_id: number): Promise<any> {
async getUser(@User('id') user_id: number): Promise<GetInfoDto> {
const user = await this.usersService.findUserById(user_id);
const followsCount = await this.usersService.getFollowsCount(user_id);
return {
Expand All @@ -142,7 +149,7 @@ export class AuthController {
@Patch('timezone')
@UseGuards(AccessTokenGuard)
@ApiOperation({ summary: '유저 타임존 설정 (완)' })
@ApiResponse({ status: 200, description: '타임존 설정 성공' })
@ApiOkResponse({ type: ResponseDto, description: '타임존 설정 성공' })
@ApiResponse({ status: 400, description: '잘못된 요청' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiBearerAuth()
Expand Down
38 changes: 38 additions & 0 deletions BE/src/auth/dto/response/get-info.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ApiProperty } from '@nestjs/swagger';

export class GetInfoDto {
@ApiProperty({
type: 'string',
example: '어린콩',
description: '닉네임',
})
nickname: string;

@ApiProperty({
type: 'string',
example: '[email protected]',
description: '이메일',
})
email: string;

@ApiProperty({
type: 'string',
example: 'https://imageurl.com',
description: '이미지 경로',
})
image_url: string;

@ApiProperty({
type: 'number',
example: 20,
description: '팔로워 수',
})
follower_count: number;

@ApiProperty({
type: 'number',
example: 10,
description: '팔로잉 수',
})
following_count: number;
}
24 changes: 24 additions & 0 deletions BE/src/auth/dto/response/update-info.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';

export class UpdateInfoDto {
@ApiProperty({
type: 'string',
example: '어린콩',
description: '닉네임',
})
nickname: string;

@ApiProperty({
type: 'string',
example: '[email protected]',
description: '이메일',
})
email: string;

@ApiProperty({
type: 'string',
example: 'https://imageurl.com',
description: '이미지 경로',
})
image_url: string;
}
9 changes: 8 additions & 1 deletion BE/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ async function bootstrap() {
const document = SwaggerModule.createDocument(app, swaggerConfig);
SwaggerModule.setup('api', app, document);

app.useGlobalPipes(new ValidationPipe());
app.useGlobalPipes(
new ValidationPipe({
transform: true, // Enable transformation
transformOptions: {
enableImplicitConversion: true, // Enable implicit conversion
},
}),
);
app.useGlobalInterceptors(new LoggingInterceptor());
app.useGlobalFilters(new HttpExceptionFilter());

Expand Down
15 changes: 15 additions & 0 deletions BE/src/mates/dto/request/pagination-query.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsNumber, Min } from 'class-validator';

export class PaginationQueryDto {
@ApiProperty({
type: 'number',
example: 1,
description: '현재 page 위치 (default: 1)',
})
@Type(() => Number) // Add this line
@IsNumber()
@Min(1)
page: number = 1;
}
31 changes: 31 additions & 0 deletions BE/src/mates/dto/response/follower-info.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ApiProperty } from '@nestjs/swagger';

export class FollowerInfoDto {
@ApiProperty({
type: 'number',
example: 1,
description: '친구 관계 id',
})
id: number;

@ApiProperty({
type: 'string',
example: '어린콩',
description: '친구 닉네임',
})
nickname: string;

@ApiProperty({
type: 'string',
example: 'https://imageurl.com',
description: '친구 이미지 경로',
})
image_url: string;

@ApiProperty({
type: 'boolean',
example: false,
description: '이미 친구관게인지',
})
is_followed: boolean;
}
24 changes: 24 additions & 0 deletions BE/src/mates/dto/response/mates-info.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';

export class MatesInfoDto {
@ApiProperty({
type: 'number',
example: 1,
description: '친구 관계 id',
})
id: number;

@ApiProperty({
type: 'string',
example: '어린콩',
description: '친구 닉네임',
})
nickname: string;

@ApiProperty({
type: 'string',
example: 'https://imageurl.com',
description: '친구 이미지 경로',
})
image_url: string;
}
2 changes: 1 addition & 1 deletion BE/src/mates/dto/response/mates.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ApiProperty } from '@nestjs/swagger';
export class MatesDto {
@ApiProperty({
type: 'number',
example: '1',
example: 1,
description: '친구 관계 id',
})
id: number;
Expand Down
46 changes: 40 additions & 6 deletions BE/src/mates/mates.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ApiBearerAuth,
ApiBody,
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiTags,
Expand All @@ -23,6 +24,9 @@ import { StatusMessageDto } from './dto/response/status-message.dto';
import { AccessTokenGuard } from 'src/auth/guard/bearer-token.guard';
import { UsersModel } from 'src/users/entity/users.entity';
import { ResponseDto } from 'src/common/response.dto';
import { PaginationQueryDto } from './dto/request/pagination-query.dto';
import { MatesInfoDto } from './dto/response/mates-info.dto';
import { FollowerInfoDto } from './dto/response/follower-info.dto';

@Controller('mates')
@ApiTags('소셜 페이지')
Expand Down Expand Up @@ -59,16 +63,32 @@ export class MatesController {
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '나를 팔로우 하는 사람들 조회하기' })
getFollowers(@User('id') user_id: number): Promise<object> {
return this.matesService.getFollowersInfo(user_id);
@ApiOkResponse({
type: FollowerInfoDto,
isArray: true,
description: '나를 팔로우한 사람들 정보(차단한 사람들은 제외)',
})
getFollowers(
@User('id') user_id: number,
@Query() query: PaginationQueryDto,
): Promise<FollowerInfoDto[]> {
return this.matesService.getFollowersInfo(user_id, query.page);
}

@Get('/followings')
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '내가 팔로우 하는 사람들 조회하기' })
getFollowings(@User('id') user_id: number): Promise<object> {
return this.matesService.getFollowingsInfo(user_id);
@ApiOkResponse({
type: MatesInfoDto,
isArray: true,
description: '팔로우 한 사람들 정보',
})
getFollowings(
@User('id') user_id: number,
@Query() query: PaginationQueryDto,
): Promise<MatesInfoDto[]> {
return this.matesService.getFollowingsInfo(user_id, query.page);
}

@Get('/status')
Expand Down Expand Up @@ -194,6 +214,9 @@ export class MatesController {
},
},
})
@ApiOkResponse({
type: ResponseDto,
})
async fixationMate(
@User('id') id: number,
@Body('following_id') following_id: number,
Expand Down Expand Up @@ -229,6 +252,9 @@ export class MatesController {
},
},
})
@ApiOkResponse({
type: ResponseDto,
})
async blockMate(
@User('id') id: number,
@Body('follower_id') follower_id: number,
Expand All @@ -246,7 +272,15 @@ export class MatesController {
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '내가 차단한 사람들 조회하기' })
getBlockedMate(@User('id') user_id: number): Promise<object> {
return this.matesService.getBlockedFollowersInfo(user_id);
@ApiOkResponse({
type: MatesInfoDto,
isArray: true,
description: '차단한 사람들 정보',
})
getBlockedMate(
@User('id') user_id: number,
@Query() query: PaginationQueryDto,
): Promise<MatesInfoDto[]> {
return this.matesService.getBlockedFollowersInfo(user_id, query.page);
}
}
Loading

0 comments on commit fea02e8

Please sign in to comment.