Skip to content

Commit

Permalink
Merge pull request #344 from boostcampwm2023/BE-feature/login
Browse files Browse the repository at this point in the history
로그인 동시성 해결
  • Loading branch information
Conut-1 authored Sep 11, 2024
2 parents 6433aec + 2e568b8 commit 49369c8
Show file tree
Hide file tree
Showing 10 changed files with 41 additions and 116 deletions.
8 changes: 8 additions & 0 deletions nestjs-BE/server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions nestjs-BE/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@types/node": "^20.3.1",
"@types/passport-jwt": "^3.0.13",
"@types/supertest": "^2.0.12",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.42.0",
Expand Down
32 changes: 3 additions & 29 deletions nestjs-BE/server/src/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ describe('AuthController', () => {
provide: UsersService,
useValue: {
findUserByEmailAndProvider: jest.fn(),
createUser: jest.fn(),
getOrCreateUser: jest.fn(),
},
},
{
provide: ProfilesService,
useValue: {
createProfile: jest.fn(),
getOrCreateProfile: jest.fn(),
},
},
{
Expand All @@ -62,7 +62,7 @@ describe('AuthController', () => {
jest
.spyOn(authService, 'getKakaoAccount')
.mockResolvedValue(kakaoUserAccountMock);
jest.spyOn(usersService, 'findUserByEmailAndProvider').mockResolvedValue({
jest.spyOn(usersService, 'getOrCreateUser').mockResolvedValue({
uuid: 'user uuid',
} as User);
jest.spyOn(authService, 'login').mockResolvedValue(tokenMock);
Expand All @@ -74,32 +74,6 @@ describe('AuthController', () => {
message: 'Success',
data: tokenMock,
});
expect(usersService.createUser).not.toHaveBeenCalled();
});

it('kakaoLogin user login first time', async () => {
const requestMock = { kakaoUserId: 0 };
const kakaoUserAccountMock = { email: 'kakao email' };
const tokenMock = {
refresh_token: 'refresh token',
access_token: 'access token',
};
jest
.spyOn(authService, 'getKakaoAccount')
.mockResolvedValue(kakaoUserAccountMock);
jest
.spyOn(usersService, 'createUser')
.mockResolvedValue({ uuid: 'user uuid' } as User);
jest.spyOn(authService, 'login').mockResolvedValue(tokenMock);

const response = controller.kakaoLogin(requestMock);

await expect(response).resolves.toEqual({
statusCode: 200,
message: 'Success',
data: tokenMock,
});
expect(usersService.createUser).toHaveBeenCalled();
});

it('kakaoLogin kakao login fail', async () => {
Expand Down
27 changes: 9 additions & 18 deletions nestjs-BE/server/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,15 @@ export class AuthController {
kakaoUserDto.kakaoUserId,
);
if (!kakaoUserAccount) throw new NotFoundException();
const email = kakaoUserAccount.email;
const user = await this.usersService.findUserByEmailAndProvider(
email,
'kakao',
);
let userUuid = user?.uuid;
if (!userUuid) {
const data = { email, provider: 'kakao' };
const createdUser = await this.usersService.createUser(data);
userUuid = createdUser.uuid;
const profileData = {
user_id: createdUser.uuid,
image: customEnv.BASE_IMAGE_URL,
nickname: '익명의 사용자',
};
await this.profilesService.createProfile(profileData);
}
const tokenData = await this.authService.login(userUuid);
const userData = { email: kakaoUserAccount.email, provider: 'kakao' };
const user = await this.usersService.getOrCreateUser(userData);
const profileData = {
user_id: user.uuid,
image: customEnv.BASE_IMAGE_URL,
nickname: '익명의 사용자',
};
await this.profilesService.getOrCreateProfile(profileData);
const tokenData = await this.authService.login(user.uuid);
return { statusCode: 200, message: 'Success', data: tokenData };
}

Expand Down
2 changes: 0 additions & 2 deletions nestjs-BE/server/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants, kakaoOauthConstants } from './constants';
import { stringify } from 'qs';
import { PrismaService } from '../prisma/prisma.service';
import { RefreshTokensService } from './refresh-tokens.service';

@Injectable()
export class AuthService {
constructor(
private jwtService: JwtService,
private refreshTokensService: RefreshTokensService,
protected prisma: PrismaService,
) {}

async getKakaoAccount(kakaoUserId: number) {
Expand Down
32 changes: 6 additions & 26 deletions nestjs-BE/server/src/profiles/profiles.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('ProfilesService', () => {
profile: {
findUnique: jest.fn(),
findMany: jest.fn(),
create: jest.fn(),
upsert: jest.fn(),
update: jest.fn(),
},
},
Expand Down Expand Up @@ -83,38 +83,18 @@ describe('ProfilesService', () => {
await expect(profiles).resolves.toEqual([]);
});

it('createProfile created', async () => {
it('getOrCreateProfile', async () => {
const data = {
user_id: generateUuid(),
image: 'www.test.com/image',
nickname: 'test nickname',
};
const testProfile = { uuid: generateUuid(), ...data };
jest.spyOn(prisma.profile, 'create').mockResolvedValue(testProfile);
const profileMock = { uuid: generateUuid(), ...data };
jest.spyOn(prisma.profile, 'upsert').mockResolvedValue(profileMock);

const profile = profilesService.createProfile(data);
const profile = profilesService.getOrCreateProfile(data);

await expect(profile).resolves.toEqual(testProfile);
});

it("createProfile user_id doesn't exists", async () => {
const data = {
user_id: generateUuid(),
image: 'www.test.com/image',
nickname: 'test nickname',
};
jest
.spyOn(prisma.profile, 'create')
.mockRejectedValue(
new PrismaClientKnownRequestError(
'Foreign key constraint failed on the field: `user_id`',
{ code: 'P2003', clientVersion: '' },
),
);

const profile = profilesService.createProfile(data);

await expect(profile).rejects.toThrow(PrismaClientKnownRequestError);
await expect(profile).resolves.toEqual(profileMock);
});

it('updateProfile updated', async () => {
Expand Down
8 changes: 5 additions & 3 deletions nestjs-BE/server/src/profiles/profiles.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ export class ProfilesService {
});
}

async createProfile(data: CreateProfileDto): Promise<Profile> {
return this.prisma.profile.create({
data: {
async getOrCreateProfile(data: CreateProfileDto): Promise<Profile> {
return this.prisma.profile.upsert({
where: { user_id: data.user_id },
update: {},
create: {
uuid: generateUuid(),
user_id: data.user_id,
image: data.image,
Expand Down
12 changes: 0 additions & 12 deletions nestjs-BE/server/src/users/dto/update-user.dto.ts

This file was deleted.

27 changes: 4 additions & 23 deletions nestjs-BE/server/src/users/users.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
import { PrismaService } from '../prisma/prisma.service';
import generateUuid from '../utils/uuid';
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';

describe('UsersService', () => {
let usersService: UsersService;
Expand All @@ -17,7 +16,7 @@ describe('UsersService', () => {
useValue: {
user: {
findUnique: jest.fn(),
create: jest.fn(),
upsert: jest.fn(),
},
},
},
Expand Down Expand Up @@ -55,37 +54,19 @@ describe('UsersService', () => {
await expect(user).resolves.toBeNull();
});

it('createUser created', async () => {
it('getOrCreateUser', async () => {
const testUser = {
uuid: generateUuid(),
email: '[email protected]',
provider: 'kakao',
};
jest.spyOn(prisma.user, 'create').mockResolvedValue(testUser);
jest.spyOn(prisma.user, 'upsert').mockResolvedValue(testUser);

const user = usersService.createUser({
const user = usersService.getOrCreateUser({
email: '[email protected]',
provider: 'kakao',
});

await expect(user).resolves.toEqual(testUser);
});

it('createUser user already exists', async () => {
jest
.spyOn(prisma.user, 'create')
.mockRejectedValue(
new PrismaClientKnownRequestError(
'Unique constraint failed on the constraint: `User_email_provider_key`',
{ code: 'P2025', clientVersion: '' },
),
);

const user = usersService.createUser({
email: '[email protected]',
provider: 'kakao',
});

await expect(user).rejects.toThrow(PrismaClientKnownRequestError);
});
});
8 changes: 5 additions & 3 deletions nestjs-BE/server/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ export class UsersService {
});
}

async createUser(data: CreateUserDto): Promise<User> {
return this.prisma.user.create({
data: {
async getOrCreateUser(data: CreateUserDto): Promise<User> {
return this.prisma.user.upsert({
where: { email_provider: { email: data.email, provider: data.provider } },
update: {},
create: {
uuid: generateUuid(),
email: data.email,
provider: data.provider,
Expand Down

0 comments on commit 49369c8

Please sign in to comment.