Skip to content

Commit

Permalink
Merge pull request #51 from PBTP/feature/response-format
Browse files Browse the repository at this point in the history
DMVM-216: feature/response format
  • Loading branch information
emibgo2 authored Dec 5, 2024
2 parents ced41c0 + 001a84c commit 8b51998
Show file tree
Hide file tree
Showing 15 changed files with 816 additions and 158 deletions.
52 changes: 38 additions & 14 deletions src/auth/presentation/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AuthService } from '../application/auth.service';
import { AuthDto, OtpRequestDto, OtpResponseDto } from './auth.dto';
import {
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
ApiResponse,
Expand All @@ -14,6 +15,7 @@ import { GroupValidation } from '../../common/validation/validation.decorator';
import { UnauthorizedException } from '@nestjs/common/exceptions';
import { Builder } from 'builder-pattern';
import { CrudGroup } from '../../common/validation/validation.data';
import { ResponseEntity } from '../../common/dto/response.entity';

@ApiTags('인증 관련 API')
@Controller('/v1/auth')
Expand All @@ -25,22 +27,28 @@ export class AuthController {
description: `Resource Server에서 제공한 식별자를 통해 고객의 정보를 확인 하고 토큰을 발급합니다.
(고객의 정보가 없을 경우 신규 고객으로 등록함)`,
})
@ApiCreatedResponse({ type: AuthDto, description: '로그인 성공' })
@ApiCreatedResponse({
type: AuthDto,
description: '로그인 성공',
})
@ApiResponse({
status: 401,
description: `Unauthorized / 요청한 Access Token이 만료되었습니다. 토큰을 갱신하세요`,
})
@Post('/login')
@GroupValidation([UserGroup.login])
async login(@Body() dto: UserDto): Promise<AuthDto> {
return await this.authService.login(dto);
async login(@Body() dto: UserDto): Promise<ResponseEntity<AuthDto>> {
return ResponseEntity.OK(await this.authService.login(dto));
}

@ApiOperation({
summary: '토큰 갱신',
description: `Refresh Token을 통해 Access Token을 재발급합니다.`,
})
@ApiCreatedResponse({ type: AuthDto, description: '갱신 성공' })
@ApiCreatedResponse({
type: AuthDto,
description: '갱신 성공',
})
@ApiResponse({
status: 401,
description:
Expand All @@ -55,7 +63,7 @@ export class AuthController {
authorization?: string;
};
},
): Promise<AuthDto> {
): Promise<ResponseEntity<AuthDto>> {
const token = req.headers.authorization?.replace('Bearer ', '');

if (!token) {
Expand All @@ -64,7 +72,10 @@ export class AuthController {
);
}

return await this.authService.tokenRefresh(token);
return ResponseEntity.CREATED(
await this.authService.tokenRefresh(token),
'refresh token 발급 완료',
);
}

@ApiOperation({
Expand All @@ -74,7 +85,10 @@ export class AuthController {
sendType을 보내면 해당 수단으로 OTP를 전송합니다. ex) sms, email\n
OTP는 10분동안 유효합니다.`,
})
@ApiCreatedResponse({ type: OtpResponseDto, description: 'OTP 발급 성공' })
@ApiCreatedResponse({
type: OtpResponseDto,
description: 'OTP 발급 성공',
})
@ApiResponse({
status: 401,
description: 'Unauthorized / 요청한 고객이 없습니다.',
Expand All @@ -91,19 +105,23 @@ export class AuthController {
async generatedOtpAndSend(
@Body() dto: OtpRequestDto,
@Query('sendType') sendType?: string,
): Promise<OtpResponseDto> {
): Promise<ResponseEntity<OtpResponseDto>> {
if (sendType) {
const generatedOtpNumber = await this.authService.sendOtp(
sendType,
dto.secret,
);

return Builder<OtpResponseDto>().otp(generatedOtpNumber).build();
return ResponseEntity.CREATED(
Builder<OtpResponseDto>().otp(generatedOtpNumber).build(),
);
}

return Builder<OtpResponseDto>()
.otp(await this.authService.generateOtp(dto.secret))
.build();
return ResponseEntity.CREATED(
Builder<OtpResponseDto>()
.otp(await this.authService.generateOtp(dto.secret))
.build(),
);
}

@ApiOperation({
Expand All @@ -115,19 +133,25 @@ export class AuthController {
status: 401,
description: 'Unauthorized / 요청한 고객이 없습니다.',
})
@ApiOkResponse({
type: OtpResponseDto,
description: 'OTP 검증 성공',
})
@GroupValidation([CrudGroup.update])
@Auth(HttpStatus.OK)
@Post('/otp/verification')
async otpVerify(
@CurrentUser() user: UserDto,
@Body() dto: OtpRequestDto,
): Promise<OtpResponseDto> {
): Promise<ResponseEntity<OtpResponseDto>> {
const validated = await this.authService.otpVerifyAndUserUpdate(
user,
dto.secret,
dto.otp,
);

return Builder<OtpResponseDto>().otp(dto.otp).verified(validated).build();
return ResponseEntity.OK(
Builder<OtpResponseDto>().otp(dto.otp).verified(validated).build(),
);
}
}
28 changes: 16 additions & 12 deletions src/business/presentation/business.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Auth, CurrentBusiness } from '../../auth/decorator/auth.decorator';
import { BusinessEntity } from '../../schemas/business.entity';
import { BusinessService } from '../application/business.service';
import { BusinessDto } from './business.dto';
import { ResponseEntity } from '../../common/dto/response.entity';
import { Builder } from 'builder-pattern';

@ApiTags('업체 관련 API')
@Controller('/v1/business')
Expand All @@ -20,17 +22,19 @@ export class BusinessController {
@Get('my')
async getMyBusinessInfo(
@CurrentBusiness() business: BusinessEntity,
): Promise<BusinessDto> {
return {
uuid: business.uuid,
authProvider: business.authProvider,
openingDate: business.openingDate,
businessId: business.businessId,
businessName: business.businessName,
businessRule: business.businessRule,
businessLocation: business.businessLocation,
businessPriceGuide: business.businessPriceGuide,
businessPhoneNumber: business.businessPhoneNumber,
};
): Promise<ResponseEntity<BusinessDto>> {
return ResponseEntity.OK(
Builder(BusinessDto)
.uuid(business.uuid)
.name(business.businessName)
.authProvider(business.authProvider)
.businessId(business.businessId)
.businessName(business.businessName)
.businessRule(business.businessRule)
.businessLocation(business.businessLocation)
.businessPriceGuide(business.businessPriceGuide)
.businessPhoneNumber(business.businessPhoneNumber)
.build(),
);
}
}
42 changes: 31 additions & 11 deletions src/chat/presentation/chat.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { Body, Controller, Get, HttpStatus, Param, Post, Query } from '@nestjs/common';
import {
Body,
Controller,
Get,
HttpStatus,
Param,
Post,
Query,
} from '@nestjs/common';
import { ChatService } from '../application/chat.service';
import { Auth, CurrentCustomer } from '../../auth/decorator/auth.decorator';
import { CustomerEntity } from '../../schemas/customer.entity';
import { CrudGroup } from '../../common/validation/validation.data';
import { GroupValidation } from '../../common/validation/validation.decorator';
import { CursorDto } from '../../common/dto/cursor.dto';
import { ChatMessageDto, ChatRoomDto } from './chat.dto';
import { ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import {
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { ResponseEntity } from '../../common/dto/response.entity';

@ApiTags('채팅방 API')
@Controller('v1/chat/room')
Expand All @@ -29,8 +43,10 @@ export class ChatController {
async createChat(
@Body() chatRoom: ChatRoomDto,
@CurrentCustomer() customer: CustomerEntity,
): Promise<ChatRoomDto> {
return await this.chatService.createChatRoom(chatRoom, customer);
): Promise<ResponseEntity<ChatRoomDto>> {
return ResponseEntity.CREATED(
await this.chatService.createChatRoom(chatRoom, customer),
);
}

@ApiOperation({
Expand All @@ -48,11 +64,13 @@ export class ChatController {
@Get()
async findChatRooms(
@CurrentCustomer() customer: CustomerEntity,
): Promise<ChatRoomDto[]> {
return await this.chatService.findChatRooms({
userId: customer.customerId,
userType: 'customer',
});
): Promise<ResponseEntity<ChatRoomDto[]>> {
return ResponseEntity.OK(
await this.chatService.findChatRooms({
userId: customer.customerId,
userType: 'customer',
}),
);
}

@ApiOperation({
Expand All @@ -73,7 +91,9 @@ export class ChatController {
@Param('chatRoomId') chatRoomId: number,
@Query() cursor: CursorDto<ChatMessageDto>,
@CurrentCustomer() customer: CustomerEntity,
): Promise<CursorDto<ChatMessageDto>> {
return await this.chatService.findMessages(chatRoomId, cursor, customer);
): Promise<ResponseEntity<CursorDto<ChatMessageDto>>> {
return ResponseEntity.OK(
await this.chatService.findMessages(chatRoomId, cursor, customer),
);
}
}
32 changes: 27 additions & 5 deletions src/common/dto/response.entity.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { HttpStatus } from '@nestjs/common';

export class ResponseEntity<T> {
statusCode: number;
message: string;
data?: T;
message: string;
statusCode: number;
secretMessage?: string;

private constructor(statusCode: number, message: string, data: T) {
private constructor(
statusCode: number,
message: string,
data: T,
secretMessage: string | undefined = undefined,
) {
this.statusCode = statusCode;
this.message = message;
this.data = data;
this.secretMessage = secretMessage;
}

static SUCCESS<T>(
Expand All @@ -19,12 +26,27 @@ export class ResponseEntity<T> {
return new ResponseEntity(statusCode, message, data);
}

static CREATED<T>(data: T, message: string = 'Created'): ResponseEntity<T> {
return ResponseEntity.SUCCESS(HttpStatus.CREATED, message, data);
}

static OK<T>(data: T, message: string = 'OK'): ResponseEntity<T> {
return ResponseEntity.SUCCESS(HttpStatus.OK, message, data);
}

static ERROR(statusCode: number, message: string): ResponseEntity<null> {
return new ResponseEntity(statusCode, message, null);
static ERROR(
statusCode: number,
message: string,
secretMessage: string | undefined = undefined,
): ResponseEntity<null> {
return new ResponseEntity(statusCode, message, null, secretMessage);
}

static BAD_REQUEST(
message: string = 'Bad Request',
secretMessage: string | undefined = undefined,
): ResponseEntity<null> {
return ResponseEntity.ERROR(HttpStatus.BAD_REQUEST, message, secretMessage);
}

static NOT_FOUND(message: string = 'Not Found'): ResponseEntity<null> {
Expand Down
25 changes: 0 additions & 25 deletions src/common/filters/entity-not-found.filter.ts

This file was deleted.

Loading

0 comments on commit 8b51998

Please sign in to comment.