Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fix][#2] 소셜로그인 반환값에 최초로그인인지 여부 추가 & [feat][#2] 소셜로그인 후에 이어지는 직군 선택 api 구현 #92

Merged
merged 5 commits into from
Aug 29, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.auth.service.AuthService;
import com.server.bbo_gak.domain.auth.service.oauth.GoogleService;
import com.server.bbo_gak.domain.user.entity.OauthProvider;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.global.annotation.AuthUser;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
Expand All @@ -32,7 +31,7 @@ public class AuthController {
@PostMapping("/social-login")
public ResponseEntity<LoginResponse> socialLogin(
@RequestHeader(SOCIAL_TOKEN_NAME) final String socialAccessToken,
@RequestParam(name = "provider") OauthProvider provider
@RequestParam(name = "provider") String provider
) {
LoginResponse response = authService.socialLogin(socialAccessToken, provider);
//TODO: 쿠키 만들어서 헤더에 넘겨야함.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
@Builder
public record LoginResponse(
String accessToken,
String refreshToken
String refreshToken,
Boolean isFirstLogin
) {

public static LoginResponse of(TokenDto tokenDto) {
public static LoginResponse of(TokenDto tokenDto, Boolean isFirstLogin) {
return LoginResponse.builder()
.accessToken(tokenDto.accessToken())
.refreshToken(tokenDto.refreshToken())
.isFirstLogin(isFirstLogin)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.server.bbo_gak.domain.auth.dto.request.LoginRequest;
import com.server.bbo_gak.domain.auth.dto.response.LoginResponse;
import com.server.bbo_gak.domain.user.entity.OauthProvider;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
Expand All @@ -11,7 +10,7 @@
@Service
public interface AuthService {

LoginResponse socialLogin(String socialAccessToken, OauthProvider provider);
LoginResponse socialLogin(String socialAccessToken, String provider);

TokenDto login(LoginRequest request);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
import com.server.bbo_gak.domain.auth.entity.AuthTestUser;
import com.server.bbo_gak.domain.auth.entity.AuthTestUserRepository;
import com.server.bbo_gak.domain.auth.service.oauth.GoogleService;
import com.server.bbo_gak.domain.user.entity.Job;
import com.server.bbo_gak.domain.user.entity.OauthProvider;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.domain.user.entity.UserRepository;
import com.server.bbo_gak.domain.user.service.UserService;
import com.server.bbo_gak.global.error.exception.BusinessException;
import com.server.bbo_gak.global.error.exception.ErrorCode;
import com.server.bbo_gak.global.error.exception.InvalidValueException;
import com.server.bbo_gak.global.error.exception.NotFoundException;
import com.server.bbo_gak.global.security.jwt.dto.AccessTokenDto;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
Expand All @@ -37,9 +37,10 @@ public class AuthServiceImpl implements AuthService {

@Override
@Transactional
public LoginResponse socialLogin(String socialAccessToken, OauthProvider provider) {
public LoginResponse socialLogin(String socialAccessToken, String provider) {

// accessToken으로 사용자 정보 얻어오기
OauthUserInfoResponse oauthUserInfo = getMemberInfo(socialAccessToken, provider);
OauthUserInfoResponse oauthUserInfo = getMemberInfo(socialAccessToken, OauthProvider.findByName(provider));

// DB에서 회원 찾기
User user = userRepository.findUserByOauthInfo(oauthUserInfo.toEntity())
Expand All @@ -51,7 +52,10 @@ public LoginResponse socialLogin(String socialAccessToken, OauthProvider provide
}
TokenDto tokenDto = jwtTokenService.createTokenDto(user.getId(), user.getRole()); // 토큰 발급

return LoginResponse.of(tokenDto);
// Job이 UNDEFINED인지 확인 (UNDEFINED라면 isFirstLogin 최초로그인값 true)
boolean isJobUndefined = user.getJob() == Job.UNDEFINE;

return LoginResponse.of(tokenDto, isJobUndefined);
}

@Override
Expand Down Expand Up @@ -100,7 +104,6 @@ public void logout(User user) {
private OauthUserInfoResponse getMemberInfo(String socialAccessToken, OauthProvider provider) {
return switch (provider) {
case GOOGLE -> googleService.getOauthUserInfo(socialAccessToken);
default -> throw new InvalidValueException(ErrorCode.INVALID_PROVIDER_TYPE);
};
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.server.bbo_gak.domain.user.controller;

import com.server.bbo_gak.domain.user.dto.request.UserJobUpdateRequest;
import com.server.bbo_gak.domain.user.dto.response.UserInfoResponse;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.domain.user.service.UserService;
import com.server.bbo_gak.global.annotation.AuthUser;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -12,10 +18,20 @@
@RequiredArgsConstructor
public class UserController {

private final UserService userService;

@GetMapping("/me")
public ResponseEntity<UserInfoResponse> memberInfo() {
return ResponseEntity.ok().body(null);
}

@PutMapping("/job")
public ResponseEntity<UserInfoResponse> updateMemberJob(
@AuthUser User user,
@RequestBody UserJobUpdateRequest request
) {
userService.updateUserJob(user, request.job());
return ResponseEntity.ok(null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.server.bbo_gak.domain.user.dto.request;

public record UserJobUpdateRequest(String job) {
}
13 changes: 12 additions & 1 deletion src/main/java/com/server/bbo_gak/domain/user/entity/Job.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.server.bbo_gak.domain.user.entity;

import com.server.bbo_gak.global.error.exception.ErrorCode;
import com.server.bbo_gak.global.error.exception.NotFoundException;
import java.util.Arrays;
import lombok.AllArgsConstructor;
import lombok.Getter;

Expand All @@ -8,7 +11,15 @@
public enum Job {
ALL("공통"),
DESIGNER("디자이너"),
DEVELOPER("개발자");
DEVELOPER("개발자"),
UNDEFINE("미설정");

private final String value;

public static Job findByValue(String value) {
return Arrays.stream(Job.values())
.filter(job -> job.getValue().equals(value))
.findFirst()
.orElseThrow((() -> new NotFoundException(ErrorCode.JOB_NOT_FOUND)));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
package com.server.bbo_gak.domain.user.entity;

import com.server.bbo_gak.global.error.exception.ErrorCode;
import com.server.bbo_gak.global.error.exception.InvalidValueException;
import java.util.Arrays;

public enum OauthProvider {
GOOGLE
GOOGLE;

public static OauthProvider findByName(String name) {
return Arrays.stream(OauthProvider.values())
.filter(oauthProvider -> oauthProvider.name().equalsIgnoreCase(name))
.findFirst()
.orElseThrow(() -> new InvalidValueException(ErrorCode.INVALID_PROVIDER_TYPE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ public static User from(OauthInfo oauthInfo) {
return User.builder()
.role(UserRole.USER)
.oauthInfo(oauthInfo)
.job(Job.DEVELOPER)
.job(Job.UNDEFINE)
.build();
}

public void updateJob(Job job) {
this.job = job;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public interface UserService {

void updateUser();

void updateUserJob(User user, String job);

void getUser();

void deleteUser();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.server.bbo_gak.domain.user.service;

import com.server.bbo_gak.domain.auth.dto.response.oauth.OauthUserInfoResponse;
import com.server.bbo_gak.domain.user.entity.Job;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.domain.user.entity.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
Expand All @@ -26,6 +28,12 @@ public void updateUser() {

}

@Transactional
public void updateUserJob(User user, String job) {
user.updateJob(Job.findByValue(job));
userRepository.save(user);
}

@Override
public void getUser() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public enum ErrorCode {

//OAuth
AUTH_GET_USER_INFO_FAILED(HttpStatus.UNAUTHORIZED, "SocialAccessToken을 통해 사용자 정보를 가져오는 데에 실패했습니다."),
INVALID_PROVIDER_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 방식의 로그인입니다."),
INVALID_PROVIDER_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 provider를 입력하셨습니다."),

PASSWORD_NOT_MATCHES(HttpStatus.BAD_REQUEST, "비밀번호를 잘못 입력하셨습니다."),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName;
import static com.epages.restdocs.apispec.ResourceDocumentation.resource;
import static org.hamcrest.Matchers.matchesPattern;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
Expand All @@ -21,9 +20,7 @@

import com.epages.restdocs.apispec.ResourceSnippetParameters;
import com.server.bbo_gak.domain.auth.dto.request.LoginRequest;
import com.server.bbo_gak.domain.auth.dto.response.LoginResponse;
import com.server.bbo_gak.domain.auth.dto.response.oauth.OauthUserInfoResponse;
import com.server.bbo_gak.domain.auth.service.AuthService;
import com.server.bbo_gak.domain.auth.service.oauth.GoogleService;
import com.server.bbo_gak.domain.user.entity.OauthProvider;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
Expand Down Expand Up @@ -146,7 +143,8 @@ class 소셜_로그인 {
)
.responseFields( // 응답 필드
fieldWithPath("accessToken").description("accessToken"),
fieldWithPath("refreshToken").description("refreshToken")
fieldWithPath("refreshToken").description("refreshToken"),
fieldWithPath("isFirstLogin").description("isFirstLogin")
)
.build()
)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.server.bbo_gak.domain.user.controller;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.server.bbo_gak.domain.user.dto.request.UserJobUpdateRequest;
import com.server.bbo_gak.global.AbstractRestDocsTests;
import com.server.bbo_gak.global.RestDocsFactory;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpMethod;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@SpringBootTest
@ActiveProfiles("test")
@Sql({"/all-data-delete.sql", "/user-test-data.sql"})
public class UserControllerTest extends AbstractRestDocsTests {
private static final String DEFAULT_URL = "/api/v1/users";

@Autowired
private RestDocsFactory restDocsFactory;

@Nested
class 직군선택 {

@Test
public void 성공() throws Exception {

//given
UserJobUpdateRequest request = new UserJobUpdateRequest("개발자");
//then
mockMvc.perform(restDocsFactory.createRequest(DEFAULT_URL + "/job", request, HttpMethod.PUT,
objectMapper))
.andExpect(status().isOk())
.andDo(restDocsFactory.getSuccessResource("[직군선택] 성공", "직군 선택", "auth", request, null));

}
}
}
28 changes: 28 additions & 0 deletions src/test/resources/user-test-data.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

delete
from refresh_token;

delete
from notification;

delete
from recruit_schedule;

delete
from recruit;

delete
from recruit_season;

delete
from users;

INSERT INTO users (deleted, created_at, update_at, user_id, dtype, email, login_id, name, password, role, job)
VALUES (false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000', 1, 'AuthTestUser', 'email', 'test', 'test',
'test123', 'USER', 'UNDEFINE');

INSERT INTO users( deleted, created_at, update_at, user_id, dtype, role, oauth_id, name, email, provider, job)
VALUES(false, '2024-07-24 21:27:20.000000', '2024-07-24 21:27:21.000000', 2, 'User', 'USER', 'oauthId', 'name', 'email', 'GOOGLE', 'UNDEFINE');

INSERT INTO refresh_token (id, token)
VALUES (1, 'abcd1234');
Loading