Skip to content

Commit 191412c

Browse files
authored
Merge pull request #323 from boostcampwm-2024/feature-be-#312
유저 cursorColor, nickname 변경 api 구현
2 parents 8ce0fd5 + 87bdbd5 commit 191412c

File tree

7 files changed

+118
-6
lines changed

7 files changed

+118
-6
lines changed

Diff for: apps/backend/src/auth/auth.controller.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
import { Controller, Get, UseGuards, Req, Res, Post } from '@nestjs/common';
1+
import {
2+
Controller,
3+
Get,
4+
UseGuards,
5+
Req,
6+
Res,
7+
Post,
8+
Patch,
9+
Body,
10+
} from '@nestjs/common';
211
import { AuthGuard } from '@nestjs/passport';
312
import { AuthService } from './auth.service';
413
import { JwtAuthGuard } from './guards/jwt-auth.guard';
514
import { Response } from 'express';
615
import { MessageResponseDto } from './dtos/messageResponse.dto';
716
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
817
import { TokenService } from './token/token.service';
18+
import { UpdateUserDto } from './dtos/UpdateUser.dto';
919

1020
export enum AuthResponseMessage {
1121
AUTH_LOGGED_OUT = '로그아웃하였습니다.',
@@ -93,6 +103,15 @@ export class AuthController {
93103
return {
94104
message: '인증된 사용자 정보',
95105
snowflakeId: user.snowflakeId,
106+
nickname: user.nickname,
107+
cursorColor: user.cursorColor,
108+
profileImage: user.profileImage,
96109
};
97110
}
111+
112+
@Patch('profile')
113+
@UseGuards(JwtAuthGuard)
114+
async updateProfile(@Req() req, @Body() body: UpdateUserDto) {
115+
await this.authService.updateUser(req.user.sub, body);
116+
}
98117
}

Diff for: apps/backend/src/auth/auth.service.spec.ts

+57-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { AuthService } from './auth.service';
33
import { UserRepository } from '../user/user.repository';
44
import { SignUpDto } from './dtos/signUp.dto';
55
import { User } from '../user/user.entity';
6-
import { Snowflake } from '@theinternetfolks/snowflake';
6+
import { UpdateUserDto } from './dtos/UpdateUser.dto';
7+
import { UserNotFoundException } from '../exception/user.exception';
78

89
describe('AuthService', () => {
910
let authService: AuthService;
@@ -17,6 +18,7 @@ describe('AuthService', () => {
1718
provide: UserRepository,
1819
useValue: {
1920
findOne: jest.fn(),
21+
findOneBy: jest.fn(),
2022
create: jest.fn(),
2123
save: jest.fn(),
2224
},
@@ -66,7 +68,9 @@ describe('AuthService', () => {
6668
provider: 'naver',
6769
6870
};
69-
const generatedSnowflakeId = Snowflake.generate(); // Snowflake.generate()의 mock 값을 준비
71+
const generated
72+
73+
flakeId = Snowflake.generate(); // Snowflake.generate()의 mock 값을 준비
7074
const newDate = new Date();
7175
const createdUser = {
7276
providerId: dto.providerId,
@@ -99,4 +103,55 @@ describe('AuthService', () => {
99103
expect(userRepository.save).toHaveBeenCalledWith(createdUser);
100104
});
101105
});
106+
107+
describe('updateUser', () => {
108+
it('사용자를 성공적으로 갱신한다', async () => {
109+
const dto: UpdateUserDto = {
110+
cursorColor: '#FFFFFF',
111+
nickname: 'new-nickname',
112+
};
113+
114+
// 현재 날짜
115+
const currentDate = new Date();
116+
const originUser = {
117+
id: 1,
118+
snowflakeId: '123456789012345678',
119+
providerId: 'kakao_12345',
120+
provider: 'kakao',
121+
122+
cursorColor: '#FF8A8A',
123+
nickname: 'origin-nickname',
124+
profileImage: 'https://example.com/profile.jpg',
125+
createdAt: currentDate,
126+
} as User;
127+
128+
const newUser = {
129+
id: 1,
130+
snowflakeId: '123456789012345678',
131+
providerId: 'kakao_12345',
132+
provider: 'kakao',
133+
134+
cursorColor: '#FFFFFF',
135+
nickname: 'new-nickname',
136+
profileImage: 'https://example.com/profile.jpg',
137+
createdAt: currentDate,
138+
};
139+
140+
jest.spyOn(userRepository, 'findOneBy').mockResolvedValue(originUser);
141+
await authService.updateUser(1, dto);
142+
expect(userRepository.save).toHaveBeenCalledWith(newUser);
143+
});
144+
145+
it('사용자가 존재하지 않으면 예외를 던진다.', async () => {
146+
const dto: UpdateUserDto = {
147+
cursorColor: '#FFFFFF',
148+
nickname: 'new-nickname',
149+
};
150+
151+
jest.spyOn(userRepository, 'findOneBy').mockResolvedValue(null);
152+
expect(authService.updateUser(1, dto)).rejects.toThrow(
153+
UserNotFoundException,
154+
);
155+
});
156+
});
102157
});

Diff for: apps/backend/src/auth/auth.service.ts

+16
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import { Injectable } from '@nestjs/common';
22
import { UserRepository } from '../user/user.repository';
33
import { User } from '../user/user.entity';
44
import { SignUpDto } from './dtos/signUp.dto';
5+
import { UpdateUserDto } from './dtos/UpdateUser.dto';
6+
import { UserNotFoundException } from '../exception/user.exception';
57
import { Snowflake } from '@theinternetfolks/snowflake';
68

9+
710
@Injectable()
811
export class AuthService {
912
constructor(private readonly userRepository: UserRepository) {}
@@ -29,4 +32,17 @@ export class AuthService {
2932
async findUserById(id: number): Promise<User | null> {
3033
return await this.userRepository.findOneBy({ id });
3134
}
35+
async updateUser(id: number, dto: UpdateUserDto) {
36+
// 유저를 찾는다.
37+
const findUser = await this.userRepository.findOneBy({ id });
38+
39+
// 유저가 없으면 오류
40+
if (!findUser) {
41+
throw new UserNotFoundException();
42+
}
43+
44+
// 유저 갱신
45+
const newPage = Object.assign({}, findUser, dto);
46+
await this.userRepository.save(newPage);
47+
}
3248
}

Diff for: apps/backend/src/auth/dtos/UpdateUser.dto.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { IsString, IsOptional } from 'class-validator';
3+
4+
export class UpdateUserDto {
5+
@ApiProperty({
6+
example: '#FF8A8A',
7+
description: '유저의 커서 색상',
8+
})
9+
@IsString()
10+
@IsOptional()
11+
cursorColor?: string;
12+
13+
@ApiProperty({
14+
example: 'nickname',
15+
description: '유저 닉네임',
16+
})
17+
@IsString()
18+
@IsOptional()
19+
nickname?: string;
20+
}

Diff for: apps/backend/src/node/node.service.spec.ts

-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { PageRepository } from '../page/page.repository';
55
import { NodeNotFoundException } from '../exception/node.exception';
66
import { Node } from './node.entity';
77
import { Page } from '../page/page.entity';
8-
import { Workspace } from '../workspace/workspace.entity';
98
import { CreateNodeDto } from './dtos/createNode.dto';
109
import { UpdateNodeDto } from './dtos/updateNode.dto';
1110
import { MoveNodeDto } from './dtos/moveNode.dto';
@@ -15,7 +14,6 @@ describe('NodeService', () => {
1514
let service: NodeService;
1615
let nodeRepository: jest.Mocked<NodeRepository>;
1716
let pageRepository: jest.Mocked<PageRepository>;
18-
let workspaceRepository: WorkspaceRepository;
1917

2018
beforeEach(async () => {
2119
const module: TestingModule = await Test.createTestingModule({

Diff for: apps/backend/src/user/user.entity.ts

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ export class User {
2525
@Column()
2626
email: string;
2727

28+
@Column({
29+
default: '#FF8A8A',
30+
})
31+
cursorColor: string;
32+
2833
@Column({ nullable: true })
2934
nickname: string;
3035

Diff for: apps/backend/src/workspace/workspace.module.ts

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { RoleModule } from '../role/role.module';
99
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
1010
import { TokenModule } from '../auth/token/token.module';
1111
import { TokenService } from '../auth/token/token.service';
12-
import { JwtService } from '@nestjs/jwt';
1312

1413
@Module({
1514
imports: [

0 commit comments

Comments
 (0)