From 13717bdf1bd661c61fd293843355eb64c7ae2304 Mon Sep 17 00:00:00 2001 From: jusung-c Date: Tue, 22 Aug 2023 04:27:09 +0900 Subject: [PATCH 01/27] =?UTF-8?q?feat(#8):=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20DTO=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/request/AuthRegisterRequest.java | 46 +++++++++++++++++++ .../auth/response/AuthRegisterResponse.java | 13 ++++++ 2 files changed, 59 insertions(+) create mode 100644 heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java index 3eb239e6..c1cae405 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java @@ -1,4 +1,50 @@ package com.heachi.auth.api.controller.auth.request; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor public class AuthRegisterRequest { + + // 문자열 값이며 비어있지 않아야 하므로 @NotEmpty 사용 -> 빈 문자열("") 불허 + @NotEmpty + @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[~!@#$%^&*()+|=])[A-Za-z\\d~!@#$%^&*()+|=]{7,16}$", + message = "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자여야 합니다") + private String platformId; + + // 플랫폼 타입 회원 정보에 필요할까?? + @NotEmpty + private String platformType; + + @NotEmpty + private String role; + + @NotEmpty + @Pattern(regexp = "^[a-zA-Z가-힣\\\\s]{2,15}", + message = "이름은 영문자, 한글, 공백포함 2글자부터 15글자까지 가능합니다.") + private String name; + + // user@example.com + @NotEmpty + @Pattern(regexp = "^[a-zA-Z0-9+-\\_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", + message = "이메일 형식을 맞춰야합니다") + private String email; + + + // 숫자 값이므로 null이 아니어야 하니까 @NotEmpty 대신 @NotNull 사용 -> 빈 문자열("") 허용 + @NotNull + @Pattern(regexp = "^\\d{11}$\n", + message = "전화번호는 11자리의 숫자로 입력해야 합니다.") + private String phoneNumber; + + @NotEmpty + private String profileImageUrl; } diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java new file mode 100644 index 00000000..0afb12b5 --- /dev/null +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java @@ -0,0 +1,13 @@ +package com.heachi.auth.api.controller.auth.response; + +import lombok.Getter; + +@Getter +public class AuthRegisterResponse { + private Long id; + private String name; + private String email; + private String phoneNumber; + private String profileImageUrl; + private String createdDateTime; +} From 4f98c1d29e47704294369b0e71e9732a07474441 Mon Sep 17 00:00:00 2001 From: jusung-c Date: Tue, 22 Aug 2023 04:49:37 +0900 Subject: [PATCH 02/27] =?UTF-8?q?feat(#8)=20MemberController=20-=20registe?= =?UTF-8?q?r=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/auth/AuthController.java | 40 +++++++++++++++++-- .../auth/request/AuthRegisterRequest.java | 1 - .../auth/api/service/auth/AuthService.java | 5 ++- .../request/AuthServiceRegisterRequest.java | 18 ++++++++- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java index 5d5bff36..60dc7b87 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java @@ -4,21 +4,30 @@ import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.admin.common.response.JsonResult; import com.heachi.auth.api.controller.auth.request.AuthRegisterRequest; +import com.heachi.auth.api.controller.auth.response.AuthRegisterResponse; import com.heachi.auth.api.service.auth.AuthService; +import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse; import com.heachi.auth.api.service.oauth.OAuthService; +import com.heachi.mysql.define.user.User; import com.heachi.mysql.define.user.constant.UserPlatformType; +import com.heachi.mysql.define.user.repository.UserRepository; import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; +import java.util.List; +import java.util.stream.Collectors; + @Slf4j @RequiredArgsConstructor @RequestMapping("/auth") @RestController public class AuthController { - private final AuthService authService; private final OAuthService oAuthService; @@ -52,7 +61,32 @@ public JsonResult login( @PostMapping("/{platformType}/register") public JsonResult register( @PathVariable("platformType") UserPlatformType platformType, - @RequestBody AuthRegisterRequest request) { - return JsonResult.successOf(); + @RequestBody @Valid AuthRegisterRequest request, + BindingResult bindingResult) { + + // AuthRegisterRequest 유효성 검사 실패시 실패한 필드의 에러 메세지를 담아 실패 리턴 + if (bindingResult.hasErrors()) { + String errorMessages = bindingResult.getFieldErrors() + .stream() + .map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage()) + .collect(Collectors.joining("\n")); + + return JsonResult.failOf(errorMessages); + } + + // Service용 DTO로 변환 + AuthServiceRegisterRequest registerRequest = AuthServiceRegisterRequest.builder() + .platformType(platformType.name()) + .platformId(request.getPlatformId()) + .role(request.getRole()) + .name(request.getName()) + .email(request.getEmail()) + .phoneNumber(request.getPhoneNumber()) + .profileImageUrl(request.getProfileImageUrl()) + .build(); + + AuthRegisterResponse registerResponse = authService.register(platformType, registerRequest); + + return JsonResult.successOf(registerResponse); } } diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java index c1cae405..58073fe8 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java @@ -20,7 +20,6 @@ public class AuthRegisterRequest { message = "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자여야 합니다") private String platformId; - // 플랫폼 타입 회원 정보에 필요할까?? @NotEmpty private String platformType; diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java index 6cd7b2dd..2dbb32fc 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java @@ -2,6 +2,7 @@ import com.heachi.admin.common.exception.ExceptionMessage; import com.heachi.admin.common.exception.oauth.OAuthException; +import com.heachi.auth.api.controller.auth.response.AuthRegisterResponse; import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse; import com.heachi.auth.api.service.jwt.JwtService; @@ -82,7 +83,9 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code .build(); } - public AuthServiceLoginResponse register(UserPlatformType platformType, AuthServiceRegisterRequest request) { + // 회원가입 후 바로 로그인된 상태가 아닌 다시 로그인 시도하도록 + // 반환 타입을 AuthServiceLoginResponse에서 AuthRegisterResponse로 바꿔봤어요 + public AuthRegisterResponse register(UserPlatformType platformType, AuthServiceRegisterRequest request) { return null; } } diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java index 5eefa5f1..5f2b3c06 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java @@ -1,5 +1,21 @@ package com.heachi.auth.api.service.auth.request; -public class AuthServiceRegisterRequest { +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthServiceRegisterRequest { + private String platformId; + private String platformType; + private String role; + private String name; + private String email; + private String phoneNumber; + private String profileImageUrl; } From b66d073b678ec33c0cb3a216985b211d007c54b9 Mon Sep 17 00:00:00 2001 From: jusung-c Date: Tue, 22 Aug 2023 05:33:55 +0900 Subject: [PATCH 03/27] =?UTF-8?q?feat(#8)=20MemberService=20-=20register?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 회원가입 후 바로 로그인된 상태가 아닌 다시 로그인 시도하도록 반환 타입을 AuthServiceLoginResponse에서 AuthRegisterResponse로 바꿔봤습니다. --- .../heachi/auth/HeachiAuthApplication.java | 2 ++ .../api/controller/auth/AuthController.java | 3 +-- .../auth/request/AuthRegisterRequest.java | 12 ++++----- .../auth/response/AuthRegisterResponse.java | 4 +++ .../auth/api/service/auth/AuthService.java | 26 ++++++++++++++++++- .../request/AuthServiceRegisterRequest.java | 4 +-- 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/HeachiAuthApplication.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/HeachiAuthApplication.java index 4add669d..8d6bd7e9 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/HeachiAuthApplication.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/HeachiAuthApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication(scanBasePackages = "com.heachi") +@EnableJpaAuditing // JPA Auditing 기능 활성화 - BaseEntity public class HeachiAuthApplication { public static void main(String[] args) { diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java index 60dc7b87..b21c8ea9 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java @@ -69,14 +69,13 @@ public JsonResult register( String errorMessages = bindingResult.getFieldErrors() .stream() .map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage()) - .collect(Collectors.joining("\n")); + .collect(Collectors.joining(", ")); return JsonResult.failOf(errorMessages); } // Service용 DTO로 변환 AuthServiceRegisterRequest registerRequest = AuthServiceRegisterRequest.builder() - .platformType(platformType.name()) .platformId(request.getPlatformId()) .role(request.getRole()) .name(request.getName()) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java index 58073fe8..1d59e2a4 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java @@ -1,5 +1,8 @@ package com.heachi.auth.api.controller.auth.request; +import com.heachi.mysql.define.user.constant.UserRole; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; @@ -20,11 +23,8 @@ public class AuthRegisterRequest { message = "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자여야 합니다") private String platformId; - @NotEmpty - private String platformType; - - @NotEmpty - private String role; + @Enumerated(EnumType.STRING) + private UserRole role; @NotEmpty @Pattern(regexp = "^[a-zA-Z가-힣\\\\s]{2,15}", @@ -40,7 +40,7 @@ public class AuthRegisterRequest { // 숫자 값이므로 null이 아니어야 하니까 @NotEmpty 대신 @NotNull 사용 -> 빈 문자열("") 허용 @NotNull - @Pattern(regexp = "^\\d{11}$\n", + @Pattern(regexp = "^\\d{11}$", message = "전화번호는 11자리의 숫자로 입력해야 합니다.") private String phoneNumber; diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java index 0afb12b5..519c4734 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java @@ -1,8 +1,12 @@ package com.heachi.auth.api.controller.auth.response; +import lombok.Builder; import lombok.Getter; +import java.time.LocalDateTime; + @Getter +@Builder public class AuthRegisterResponse { private Long id; private String name; diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java index 2dbb32fc..0313d70c 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java @@ -14,6 +14,7 @@ import com.heachi.mysql.define.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -31,6 +32,7 @@ public class AuthService { private final UserRepository userRepository; private final OAuthService oAuthService; private final JwtService jwtService; + private final BCryptPasswordEncoder passwordEncoder; private static final String ROLE_CLAIM = "role"; private static final String NAME_CLAIM = "name"; @@ -86,6 +88,28 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code // 회원가입 후 바로 로그인된 상태가 아닌 다시 로그인 시도하도록 // 반환 타입을 AuthServiceLoginResponse에서 AuthRegisterResponse로 바꿔봤어요 public AuthRegisterResponse register(UserPlatformType platformType, AuthServiceRegisterRequest request) { - return null; + + User saveUser = User.builder() + .platformId(passwordEncoder.encode(request.getPlatformId())) + .platformType(platformType) + .role(request.getRole()) + .name(request.getName()) + .email(request.getEmail()) + .phoneNumber(request.getPhoneNumber()) + .profileImageUrl(request.getProfileImageUrl()) + .build(); + + User savedUser = userRepository.save(saveUser); + + AuthRegisterResponse registerResponse = AuthRegisterResponse.builder() + .id(savedUser.getId()) + .name(savedUser.getName()) + .email(savedUser.getEmail()) + .phoneNumber(savedUser.getPhoneNumber()) + .profileImageUrl(savedUser.getProfileImageUrl()) + .createdDateTime(savedUser.getCreatedDateTime().toString()) + .build(); + + return registerResponse; } } diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java index 5f2b3c06..1502777f 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java @@ -1,6 +1,7 @@ package com.heachi.auth.api.service.auth.request; +import com.heachi.mysql.define.user.constant.UserRole; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -12,8 +13,7 @@ @AllArgsConstructor public class AuthServiceRegisterRequest { private String platformId; - private String platformType; - private String role; + private UserRole role; private String name; private String email; private String phoneNumber; From b313a75a4b4e14043a6333dc057de76b538ae76d Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Thu, 24 Aug 2023 11:37:03 +0900 Subject: [PATCH 04/27] =?UTF-8?q?refactor(#38):=20Validator=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/auth/AuthController.java | 13 +------------ .../config/advice/GlobalExceptionHandler.java | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java index b21c8ea9..e20e008c 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java @@ -61,18 +61,7 @@ public JsonResult login( @PostMapping("/{platformType}/register") public JsonResult register( @PathVariable("platformType") UserPlatformType platformType, - @RequestBody @Valid AuthRegisterRequest request, - BindingResult bindingResult) { - - // AuthRegisterRequest 유효성 검사 실패시 실패한 필드의 에러 메세지를 담아 실패 리턴 - if (bindingResult.hasErrors()) { - String errorMessages = bindingResult.getFieldErrors() - .stream() - .map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage()) - .collect(Collectors.joining(", ")); - - return JsonResult.failOf(errorMessages); - } + @Valid @RequestBody AuthRegisterRequest request) { // Service용 DTO로 변환 AuthServiceRegisterRequest registerRequest = AuthServiceRegisterRequest.builder() diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/advice/GlobalExceptionHandler.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/advice/GlobalExceptionHandler.java index 26113f42..b04a976d 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/advice/GlobalExceptionHandler.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/advice/GlobalExceptionHandler.java @@ -1,16 +1,31 @@ package com.heachi.auth.config.advice; import com.heachi.admin.common.response.JsonResult; -import org.springframework.http.HttpStatus; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.stream.Collectors; + @RestControllerAdvice public class GlobalExceptionHandler { + @ExceptionHandler(BindException.class) + public JsonResult bindException(BindException e) { + return JsonResult.failOf( + e.getBindingResult() + .getFieldErrors() + .stream() + .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()) + .collect(Collectors.joining(", ")) + ); + } + @ExceptionHandler(Exception.class) public JsonResult exception(Exception e) { return JsonResult.failOf(e.getMessage()); } + + } From 4c1da4bd27413bbf10fd7e8b6d57c590d740edef Mon Sep 17 00:00:00 2001 From: jusung-c Date: Sat, 26 Aug 2023 16:56:52 +0900 Subject: [PATCH 05/27] =?UTF-8?q?refactor(#38):=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=94=84=EB=A1=9C=EC=84=B8=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20login=20=ED=94=84=EB=A1=9C=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=EC=97=90=EC=84=9C=20UNAUTH=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EC=9D=98=20token=EC=9D=84=20null=EB=A1=9C=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=96=88=EB=8D=98=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=ED=95=B4=EC=84=9C=20=EB=8B=A4=EB=A5=B8=20role?= =?UTF-8?q?=EA=B3=BC=20=EB=8F=99=EC=9D=BC=ED=95=98=EA=B2=8C=20token=20?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=ED=9B=84=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EC=97=AC=20API=EC=9D=98=20response=20=EC=9D=BC=EA=B4=80?= =?UTF-8?q?=EC=84=B1=20=EC=9C=A0=EC=A7=80=20-=20UNAUTH=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EC=99=B8=EC=9D=98=20role?= =?UTF-8?q?=EC=9D=80=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=ED=95=A0=20=EC=88=98=20=EC=97=86=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?Security=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/heachi/auth/api/service/auth/AuthService.java | 8 -------- .../com/heachi/auth/config/security/SecurityConfig.java | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java index 0313d70c..c131d0c2 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java @@ -61,14 +61,6 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code // 기존 회원의 경우 name, profileImageUrl 변하면 update findUser.updateProfile(loginResponse.getName(), loginResponse.getProfileImageUrl()); - // 인증이 완료되지 않은 사용자(UNAUTH) 처리 - if (findUser.getRole() == UserRole.UNAUTH) { - return AuthServiceLoginResponse.builder() - .token(null) - .role(UserRole.UNAUTH) - .build(); - } - // JWT 토큰 생성을 위한 claims 생성 HashMap claims = new HashMap<>(); claims.put(ROLE_CLAIM, findUser.getRole().name()); diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java index 15e70cd3..c055536d 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java @@ -27,6 +27,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti authorizeHttpRequests // UnAuth Area .requestMatchers("/auth/**").permitAll() + .requestMatchers("/auth/**/register").hasAnyAuthority("UNAUTH") // Swagger 3.0 .requestMatchers("/v3/api-docs/**", "/swagger-ui/**").permitAll() // Others From 7b25d6a13e03355e8d0d62bd53b00e56350a97cb Mon Sep 17 00:00:00 2001 From: jusung-c Date: Sun, 27 Aug 2023 00:31:41 +0900 Subject: [PATCH 06/27] =?UTF-8?q?refactor(#38):=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=94=84=EB=A1=9C=EC=84=B8=EC=8A=A4=20?= =?UTF-8?q?=ED=94=BC=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 회원가입 완료시 로그인된 상태가 되도록 token과 role을 담은 DTO 리턴 - 회원가입 예외 처리 --- .../api/controller/auth/AuthController.java | 11 +-- .../auth/response/AuthRegisterResponse.java | 17 ---- .../auth/api/service/auth/AuthService.java | 95 ++++++++++++------- .../controller/auth/AuthControllerTest.java | 66 +++++++++---- .../com/heachi/mysql/define/user/User.java | 15 +++ .../common/exception/ExceptionMessage.java | 6 +- 6 files changed, 129 insertions(+), 81 deletions(-) delete mode 100644 heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java index e20e008c..684283a3 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java @@ -4,25 +4,17 @@ import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.admin.common.response.JsonResult; import com.heachi.auth.api.controller.auth.request.AuthRegisterRequest; -import com.heachi.auth.api.controller.auth.response.AuthRegisterResponse; import com.heachi.auth.api.service.auth.AuthService; import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse; import com.heachi.auth.api.service.oauth.OAuthService; -import com.heachi.mysql.define.user.User; import com.heachi.mysql.define.user.constant.UserPlatformType; -import com.heachi.mysql.define.user.repository.UserRepository; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.BindingResult; -import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; -import java.util.List; -import java.util.stream.Collectors; - @Slf4j @RequiredArgsConstructor @RequestMapping("/auth") @@ -73,7 +65,8 @@ public JsonResult register( .profileImageUrl(request.getProfileImageUrl()) .build(); - AuthRegisterResponse registerResponse = authService.register(platformType, registerRequest); + // 회원가입이 완료되면 로그인된 상태가 되도록 JWT 토큰과 role을 담아 반환 + AuthServiceLoginResponse registerResponse = authService.register(platformType, registerRequest); return JsonResult.successOf(registerResponse); } diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java deleted file mode 100644 index 519c4734..00000000 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/AuthRegisterResponse.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.heachi.auth.api.controller.auth.response; - -import lombok.Builder; -import lombok.Getter; - -import java.time.LocalDateTime; - -@Getter -@Builder -public class AuthRegisterResponse { - private Long id; - private String name; - private String email; - private String phoneNumber; - private String profileImageUrl; - private String createdDateTime; -} diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java index c131d0c2..563c6b62 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java @@ -2,7 +2,6 @@ import com.heachi.admin.common.exception.ExceptionMessage; import com.heachi.admin.common.exception.oauth.OAuthException; -import com.heachi.auth.api.controller.auth.response.AuthRegisterResponse; import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse; import com.heachi.auth.api.service.jwt.JwtService; @@ -12,6 +11,9 @@ import com.heachi.mysql.define.user.constant.UserPlatformType; import com.heachi.mysql.define.user.constant.UserRole; import com.heachi.mysql.define.user.repository.UserRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -19,8 +21,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; -import java.util.Objects; -import java.util.Optional; @Slf4j @@ -33,6 +33,8 @@ public class AuthService { private final OAuthService oAuthService; private final JwtService jwtService; private final BCryptPasswordEncoder passwordEncoder; + @PersistenceContext + private EntityManager entityManager; private static final String ROLE_CLAIM = "role"; private static final String NAME_CLAIM = "name"; @@ -61,47 +63,70 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code // 기존 회원의 경우 name, profileImageUrl 변하면 update findUser.updateProfile(loginResponse.getName(), loginResponse.getProfileImageUrl()); - // JWT 토큰 생성을 위한 claims 생성 - HashMap claims = new HashMap<>(); - claims.put(ROLE_CLAIM, findUser.getRole().name()); - claims.put(NAME_CLAIM, findUser.getName()); - claims.put(PROFILE_IMAGE_CLAIM, findUser.getProfileImageUrl()); - - // JWT 토큰 생성 (claims, UserDetails) - final String token = jwtService.generateToken(claims, findUser); + // JWT 토큰 발급 + final String token = createJwtToken(findUser); - // 로그인 반환 객체 생성 return AuthServiceLoginResponse.builder() .token(token) .role(findUser.getRole()) .build(); } - // 회원가입 후 바로 로그인된 상태가 아닌 다시 로그인 시도하도록 - // 반환 타입을 AuthServiceLoginResponse에서 AuthRegisterResponse로 바꿔봤어요 - public AuthRegisterResponse register(UserPlatformType platformType, AuthServiceRegisterRequest request) { - - User saveUser = User.builder() - .platformId(passwordEncoder.encode(request.getPlatformId())) - .platformType(platformType) - .role(request.getRole()) - .name(request.getName()) - .email(request.getEmail()) - .phoneNumber(request.getPhoneNumber()) - .profileImageUrl(request.getProfileImageUrl()) - .build(); + @Transactional + public AuthServiceLoginResponse register(UserPlatformType platformType, AuthServiceRegisterRequest request) { + try { + User findUser = userRepository.findByEmail(request.getEmail()).orElseThrow(() -> { + // UNAUTH인 토큰을 받고 회원 탈퇴 후 그 토큰으로 회원가입 요청시 예외 처리 + throw new OAuthException(ExceptionMessage.OAUTH_INVALID_REGISTER); + }); + + // User findUser = userRepository.findByEmail(request.getEmail()).get(); + + // 회원가입 정보 DB 반영 + findUser.updateRegister(passwordEncoder.encode(request.getPlatformId()), + request.getRole(), + request.getName(), + request.getEmail(), + request.getPhoneNumber(), + request.getProfileImageUrl()); + + // 수정된 회원정보 조회 + String jpql = "SELECT u FROM USERS u WHERE u.email = :email"; + User updateUser = entityManager.createQuery(jpql, User.class) + .setParameter("email", request.getEmail()) + .getSingleResult(); + + // UNAUTH 토큰으로 회원가입을 요청했지만 이미 회원가입시 update되어 UNAUTH가 아닌 사용자 예외 처리 + if (updateUser.getRole() != UserRole.UNAUTH) { + throw new OAuthException(ExceptionMessage.OAUTH_UNAUTH_DUPLICATE_REGISTER); + } + + // 바뀐 회원 정보로 JWT 토큰 재발급 + final String token = createJwtToken(updateUser); + + return AuthServiceLoginResponse.builder() + .token(token) + .role(findUser.getRole()) + .build(); + + } catch (OAuthException e) { + throw new RuntimeException(e.getMessage()); + } catch (Exception e) { + throw new RuntimeException("exception!!!"); + } + } - User savedUser = userRepository.save(saveUser); + private String createJwtToken(User user) { + // JWT 토큰 생성을 위한 claims 생성 + HashMap claims = new HashMap<>(); + claims.put(ROLE_CLAIM, user.getRole().name()); + claims.put(NAME_CLAIM, user.getName()); + claims.put(PROFILE_IMAGE_CLAIM, user.getProfileImageUrl()); - AuthRegisterResponse registerResponse = AuthRegisterResponse.builder() - .id(savedUser.getId()) - .name(savedUser.getName()) - .email(savedUser.getEmail()) - .phoneNumber(savedUser.getPhoneNumber()) - .profileImageUrl(savedUser.getProfileImageUrl()) - .createdDateTime(savedUser.getCreatedDateTime().toString()) - .build(); + // JWT 토큰 생성 (claims, UserDetails) + final String token = jwtService.generateToken(claims, user); - return registerResponse; + // 로그인 반환 객체 생성 + return token; } } diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java index dfd6ea9b..a3423ce3 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java @@ -1,11 +1,16 @@ package com.heachi.auth.api.controller.auth; +import com.fasterxml.jackson.databind.json.JsonMapper; import com.heachi.admin.common.exception.ExceptionMessage; import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.auth.TestConfig; +import com.heachi.auth.api.controller.auth.request.AuthRegisterRequest; +import com.heachi.auth.api.service.auth.AuthService; +import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; import com.heachi.auth.api.service.oauth.OAuthService; import com.heachi.auth.api.service.oauth.response.OAuthResponse; import com.heachi.mysql.define.user.constant.UserPlatformType; +import com.heachi.mysql.define.user.constant.UserRole; import com.heachi.mysql.define.user.repository.UserRepository; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -14,19 +19,16 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static com.heachi.mysql.define.user.constant.UserPlatformType.*; -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @AutoConfigureMockMvc @SpringBootTest @@ -38,6 +40,9 @@ class AuthControllerTest extends TestConfig { @MockBean private OAuthService oAuthService; + @MockBean + private AuthService authService; + @Autowired private UserRepository userRepository; @@ -155,26 +160,49 @@ void naverLoginFailTest() throws Exception { } @Test - @DisplayName("네이버 로그인시 State 값이 현재 session Id와 일치하지 않으면, OAuthException 예외가 발생한다.") - void naverLoginFailWhenInvalidState() throws Exception { - String code = "code"; - String state = "invalidState"; + @DisplayName("회원가입 성공 테스트") + void registerSuccessTest() throws Exception { + // given + // 유효성 검사 통과하는 request + AuthServiceRegisterRequest request = AuthServiceRegisterRequest.builder() + .platformId("test1234@@") + .role(UserRole.USER) + .name("testUser") + .email("testUser@example.com") + .phoneNumber("01012341234") + .profileImageUrl("https://example.com") + .build(); - // invalidState 값을 사용해 login을 시도하면 Exception 발생함 - given(oAuthService.login(NAVER, code, state)) - .willThrow(new OAuthException(ExceptionMessage.OAUTH_INVALID_STATE)); + mockMvc.perform( + post("/auth/KAKAO/register") + .contentType(MediaType.APPLICATION_JSON) + .content(JsonMapper.builder().build().writeValueAsString(request))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.resCode").value(200)) + .andExpect(jsonPath("$.resMsg").value("OK")); + } + @Test + @DisplayName("회원가입 실패 테스트") + void registerFailTest() throws Exception { + // given + // 유효성 검사 실패하는 request + AuthRegisterRequest request = AuthRegisterRequest.builder() + .platformId("test1234@@") + .role(null) + .name("testUser") + .email("1-203-102-3") // 잘못된 형식의 email + .phoneNumber("01012341234") + .profileImageUrl("https://naver.com") + .build(); - // when mockMvc.perform( - get("/auth/NAVER/login") - .param("code", code) - .param("state", state)) - - // then + post("/auth/KAKAO/register") + .contentType(MediaType.APPLICATION_JSON) + .content(JsonMapper.builder().build().writeValueAsString(request))) + // .andExpect(status().isBadRequest()); .andExpect(status().isOk()) .andExpect(jsonPath("$.resCode").value(400)) - .andExpect(jsonPath("$.resMsg").value(ExceptionMessage.OAUTH_INVALID_STATE.getText())) - .andDo(print()); + .andExpect(jsonPath("$.resMsg").value("email: 이메일 형식을 맞춰야합니다")); } } \ No newline at end of file diff --git a/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java b/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java index 8ae361d5..2ae6461a 100644 --- a/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java +++ b/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java @@ -46,6 +46,21 @@ public void updateProfile(String name, String profileImageUrl) { this.name = name; } + public void updateRegister(String platformId, + UserRole role, + String name, + String email, + String phoneNumber, + String profileImageUrl) { + this.platformId = platformId; + this.role = role; + this.name = name; + this.email = email; + this.phoneNumber = phoneNumber; + this.profileImageUrl = profileImageUrl; + + } + @Builder private User(String platformId, UserPlatformType platformType, UserRole role, String name, String email, String phoneNumber, String profileImageUrl) { this.platformId = platformId; diff --git a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java index 12bdb8eb..bacb83b3 100644 --- a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java +++ b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java @@ -16,7 +16,11 @@ public enum ExceptionMessage { // OAuthException OAUTH_INVALID_STATE("STATE 검증에 실패하였습니다."), OAUTH_INVALID_TOKEN_URL("tokenURL이 정확하지 않습니다."), - OAUTH_INVALID_ACCESS_TOKEN("잘못된 access_token 입니다."); + OAUTH_INVALID_ACCESS_TOKEN("잘못된 access_token 입니다."), + + // RegisterException + OAUTH_INVALID_REGISTER("잘못된 회원가입 요청입니다."), + OAUTH_UNAUTH_DUPLICATE_REGISTER("UNAUTH인 사용자만 회원가입이 가능합니다."); private final String text; From e8316d0ace92da85fbd0dce1a1f050d1dbccb588 Mon Sep 17 00:00:00 2001 From: jusung-c Date: Sun, 27 Aug 2023 00:53:49 +0900 Subject: [PATCH 07/27] =?UTF-8?q?refactor(#38):=20UNAUTH=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UNAUTH가 아닌 사용자 예외 처리 부분 수정 --- .../com/heachi/auth/api/service/auth/AuthService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java index 563c6b62..51dc8e4d 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java @@ -82,6 +82,11 @@ public AuthServiceLoginResponse register(UserPlatformType platformType, AuthServ // User findUser = userRepository.findByEmail(request.getEmail()).get(); + // UNAUTH 토큰으로 회원가입을 요청했지만 이미 update되어 UNAUTH가 아닌 사용자 예외 처리 + if (findUser.getRole() != UserRole.UNAUTH) { + throw new OAuthException(ExceptionMessage.OAUTH_UNAUTH_DUPLICATE_REGISTER); + } + // 회원가입 정보 DB 반영 findUser.updateRegister(passwordEncoder.encode(request.getPlatformId()), request.getRole(), @@ -96,11 +101,6 @@ public AuthServiceLoginResponse register(UserPlatformType platformType, AuthServ .setParameter("email", request.getEmail()) .getSingleResult(); - // UNAUTH 토큰으로 회원가입을 요청했지만 이미 회원가입시 update되어 UNAUTH가 아닌 사용자 예외 처리 - if (updateUser.getRole() != UserRole.UNAUTH) { - throw new OAuthException(ExceptionMessage.OAUTH_UNAUTH_DUPLICATE_REGISTER); - } - // 바뀐 회원 정보로 JWT 토큰 재발급 final String token = createJwtToken(updateUser); From ee19f167e4fda6f4349056fc5d20fdba73215ad2 Mon Sep 17 00:00:00 2001 From: jusung-c Date: Sun, 27 Aug 2023 01:51:33 +0900 Subject: [PATCH 08/27] =?UTF-8?q?test(#38):=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 필드 유효성 검사 테스트 (성공, 실패) - UNAUTH 미가입자 회원가입 테스트 (성공, 실패) --- .../api/service/auth/AuthServiceTest.java | 112 ++++++++++++------ 1 file changed, 79 insertions(+), 33 deletions(-) diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java index 29258378..ac69ed93 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java @@ -1,6 +1,8 @@ package com.heachi.auth.api.service.auth; +import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.auth.TestConfig; +import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse; import com.heachi.auth.api.service.jwt.JwtService; import com.heachi.auth.api.service.oauth.OAuthService; @@ -20,6 +22,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import java.util.List; +import java.util.Optional; import static com.heachi.mysql.define.user.constant.UserPlatformType.KAKAO; import static org.assertj.core.api.Assertions.*; @@ -85,39 +88,6 @@ void loginSuccess() throws Exception { .contains("김민수", email, "google.com", UserRole.USER); } - @Test - @DisplayName("User로 등록되어있더라도, 인증이 완료되지 않은 유저(UserRole = UNAUTH)는 로그인할 수 없다.") - void loginWhenUnAuthUser() { - // given - UserPlatformType platformType = UserPlatformType.KAKAO; - String code = "임의의 코드입니다."; - String state = "임의의 state입니다."; - String email = "kimminsu@dankook.ac.kr"; - - User user = User.builder() - .name("김민수") - .email(email) - .role(UserRole.UNAUTH) - .build(); - userRepository.save(user); - - OAuthResponse oAuthResponse = OAuthResponse.builder() - .platformId("123") - .email(email) - .name("김민수") - .profileImageUrl("google.com") - .build(); - - // when - when(oAuthService.login(any(UserPlatformType.class), any(String.class), any(String.class))).thenReturn(oAuthResponse); // Mocking - - // then - assertAll(() -> { - assertThat(authService.login(platformType, code, state).getRole()).isEqualTo(UserRole.UNAUTH); - assertThat(authService.login(platformType, code, state).getToken()).isNull(); - }); - } - @Test @DisplayName("로그인이 완료된 사용자의 jwt 토큰에는 알맞은 Claims가 들어가있어야 한다.") void loginTokenValidClaims() { @@ -223,4 +193,80 @@ void loginUpdateUserInfoWhenUserInfoChanged() { assertThat(findUser.getProfileImageUrl()).isEqualTo("google.com"); }); } + + @Test + @DisplayName("UNAUTH 미가입자 회원가입 성공 테스트") + public void registerUnauthUserSuccessTest() { + UserPlatformType platformType = KAKAO; + String email = "user@example.com"; + String platformId = "qwer1234@"; + String name = "tesrUser"; + String phoneNumber = "01234567890"; + String profileImageUrl = "https://example.com/profile.jpg"; + + // UNAUTH 사용자 저장 + User unauthUser = User.builder() + .platformId(platformId) + .platformType(platformType) + .role(UserRole.UNAUTH) + .email(email) + .build(); + + User findUser = userRepository.save(unauthUser); + + // 회원가입 요청 생성 (CENTER) + AuthServiceRegisterRequest request = AuthServiceRegisterRequest.builder() + .platformId(platformId) + .role(UserRole.CENTER) + .profileImageUrl(profileImageUrl) + .phoneNumber(phoneNumber) + .email(email) + .name(name) + .build(); + + // when + AuthServiceLoginResponse response = authService.register(platformType, request); + boolean tokenValid = jwtService.isTokenValid(response.getToken(), findUser); // 발행한 토큰 검증 + + // then + assertEquals(UserRole.CENTER, response.getRole()); + assertThat(tokenValid).isTrue(); + } + + @Test + @DisplayName("UNAUTH 미가입자 회원가입 실패 테스트") + public void registerUnauthUserFailTest() { + UserPlatformType platformType = KAKAO; + String email = "user@example.com"; + String platformId = "qwer1234@"; + String name = "tesrUser"; + String phoneNumber = "01234567890"; + String profileImageUrl = "https://example.com/profile.jpg"; + + // UNAUTH 사용자 저장 + User unauthUser = User.builder() + .platformId(platformId) + .platformType(platformType) + .role(UserRole.CENTER) // UNAUTH 미가입자가 아님 + .email(email) + .build(); + User findUser = userRepository.save(unauthUser); + + // 회원가입 요청 생성 (CENTER) + AuthServiceRegisterRequest request = AuthServiceRegisterRequest.builder() + .platformId(platformId) + .role(UserRole.CENTER) + .profileImageUrl(profileImageUrl) + .phoneNumber(phoneNumber) + .email(email) + .name(name) + .build(); + + // when + + // then + assertThrows(RuntimeException.class, () -> { + authService.register(platformType, request); + }); + } } \ No newline at end of file From 3fc56bae271851427c81449d6b284e73f8e3433f Mon Sep 17 00:00:00 2001 From: jusung-c Date: Sun, 27 Aug 2023 20:59:09 +0900 Subject: [PATCH 09/27] =?UTF-8?q?refactor(#38):=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=94=84=EB=A1=9C=EC=84=B8=EC=8A=A4=20?= =?UTF-8?q?=ED=94=BC=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81=20(2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 회원가입시 받을 파라미터 수정 - url에서 platformType 받는 과정 제거 - 한 트랜잭션 안에서 반복된 조회문 제거 - AuthServiceRegisterRequest의 of 메소드로 리팩토링 - Exception 수정 --- .../api/controller/auth/AuthController.java | 16 ++-------- .../auth/request/AuthRegisterRequest.java | 23 ++------------- .../auth/api/service/auth/AuthService.java | 28 +++++------------- .../request/AuthServiceRegisterRequest.java | 14 ++++++--- .../controller/auth/AuthControllerTest.java | 6 ---- .../api/service/auth/AuthServiceTest.java | 29 ++++++++++--------- .../com/heachi/mysql/define/user/User.java | 12 +------- .../common/exception/ExceptionMessage.java | 7 ++--- .../common/exception/auth/AuthException.java | 11 +++++++ 9 files changed, 52 insertions(+), 94 deletions(-) create mode 100644 heachi-support/common/src/main/java/com/heachi/admin/common/exception/auth/AuthException.java diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java index 684283a3..9f24c6e9 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java @@ -50,23 +50,11 @@ public JsonResult login( return JsonResult.successOf(loginResponse); } - @PostMapping("/{platformType}/register") + @PostMapping("/register") public JsonResult register( - @PathVariable("platformType") UserPlatformType platformType, @Valid @RequestBody AuthRegisterRequest request) { - // Service용 DTO로 변환 - AuthServiceRegisterRequest registerRequest = AuthServiceRegisterRequest.builder() - .platformId(request.getPlatformId()) - .role(request.getRole()) - .name(request.getName()) - .email(request.getEmail()) - .phoneNumber(request.getPhoneNumber()) - .profileImageUrl(request.getProfileImageUrl()) - .build(); - - // 회원가입이 완료되면 로그인된 상태가 되도록 JWT 토큰과 role을 담아 반환 - AuthServiceLoginResponse registerResponse = authService.register(platformType, registerRequest); + AuthServiceLoginResponse registerResponse = authService.register(AuthServiceRegisterRequest.of(request)); return JsonResult.successOf(registerResponse); } diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java index 1d59e2a4..5a9d88dd 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java @@ -16,34 +16,15 @@ @NoArgsConstructor @AllArgsConstructor public class AuthRegisterRequest { - - // 문자열 값이며 비어있지 않아야 하므로 @NotEmpty 사용 -> 빈 문자열("") 불허 @NotEmpty - @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[~!@#$%^&*()+|=])[A-Za-z\\d~!@#$%^&*()+|=]{7,16}$", - message = "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자여야 합니다") - private String platformId; + private String email; @Enumerated(EnumType.STRING) private UserRole role; - @NotEmpty - @Pattern(regexp = "^[a-zA-Z가-힣\\\\s]{2,15}", - message = "이름은 영문자, 한글, 공백포함 2글자부터 15글자까지 가능합니다.") - private String name; - - // user@example.com - @NotEmpty - @Pattern(regexp = "^[a-zA-Z0-9+-\\_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", - message = "이메일 형식을 맞춰야합니다") - private String email; - - // 숫자 값이므로 null이 아니어야 하니까 @NotEmpty 대신 @NotNull 사용 -> 빈 문자열("") 허용 @NotNull @Pattern(regexp = "^\\d{11}$", message = "전화번호는 11자리의 숫자로 입력해야 합니다.") private String phoneNumber; - - @NotEmpty - private String profileImageUrl; -} +} \ No newline at end of file diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java index 51dc8e4d..861aa9c8 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java @@ -1,6 +1,7 @@ package com.heachi.auth.api.service.auth; import com.heachi.admin.common.exception.ExceptionMessage; +import com.heachi.admin.common.exception.auth.AuthException; import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse; @@ -73,36 +74,23 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code } @Transactional - public AuthServiceLoginResponse register(UserPlatformType platformType, AuthServiceRegisterRequest request) { + public AuthServiceLoginResponse register(AuthServiceRegisterRequest request) { try { User findUser = userRepository.findByEmail(request.getEmail()).orElseThrow(() -> { // UNAUTH인 토큰을 받고 회원 탈퇴 후 그 토큰으로 회원가입 요청시 예외 처리 - throw new OAuthException(ExceptionMessage.OAUTH_INVALID_REGISTER); + throw new AuthException(ExceptionMessage.AUTH_INVALID_REGISTER); }); - // User findUser = userRepository.findByEmail(request.getEmail()).get(); - // UNAUTH 토큰으로 회원가입을 요청했지만 이미 update되어 UNAUTH가 아닌 사용자 예외 처리 if (findUser.getRole() != UserRole.UNAUTH) { - throw new OAuthException(ExceptionMessage.OAUTH_UNAUTH_DUPLICATE_REGISTER); + throw new AuthException(ExceptionMessage.AUTH_DUPLICATE_UNAUTH_REGISTER); } // 회원가입 정보 DB 반영 - findUser.updateRegister(passwordEncoder.encode(request.getPlatformId()), - request.getRole(), - request.getName(), - request.getEmail(), - request.getPhoneNumber(), - request.getProfileImageUrl()); - - // 수정된 회원정보 조회 - String jpql = "SELECT u FROM USERS u WHERE u.email = :email"; - User updateUser = entityManager.createQuery(jpql, User.class) - .setParameter("email", request.getEmail()) - .getSingleResult(); - - // 바뀐 회원 정보로 JWT 토큰 재발급 - final String token = createJwtToken(updateUser); + findUser.updateRegister(request.getRole(), request.getPhoneNumber()); + + // JWT 토큰 재발급 + final String token = createJwtToken(findUser); return AuthServiceLoginResponse.builder() .token(token) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java index 1502777f..9e748a76 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/request/AuthServiceRegisterRequest.java @@ -1,6 +1,7 @@ package com.heachi.auth.api.service.auth.request; +import com.heachi.auth.api.controller.auth.request.AuthRegisterRequest; import com.heachi.mysql.define.user.constant.UserRole; import lombok.AllArgsConstructor; import lombok.Builder; @@ -12,10 +13,15 @@ @NoArgsConstructor @AllArgsConstructor public class AuthServiceRegisterRequest { - private String platformId; - private UserRole role; - private String name; private String email; + private UserRole role; private String phoneNumber; - private String profileImageUrl; + + public static AuthServiceRegisterRequest of(AuthRegisterRequest request) { + return AuthServiceRegisterRequest.builder() + .email(request.getEmail()) + .role(request.getRole()) + .phoneNumber(request.getPhoneNumber()) + .build(); + } } diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java index a3423ce3..3b35ad51 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java @@ -165,12 +165,9 @@ void registerSuccessTest() throws Exception { // given // 유효성 검사 통과하는 request AuthServiceRegisterRequest request = AuthServiceRegisterRequest.builder() - .platformId("test1234@@") .role(UserRole.USER) - .name("testUser") .email("testUser@example.com") .phoneNumber("01012341234") - .profileImageUrl("https://example.com") .build(); mockMvc.perform( @@ -188,12 +185,9 @@ void registerFailTest() throws Exception { // given // 유효성 검사 실패하는 request AuthRegisterRequest request = AuthRegisterRequest.builder() - .platformId("test1234@@") .role(null) - .name("testUser") .email("1-203-102-3") // 잘못된 형식의 email .phoneNumber("01012341234") - .profileImageUrl("https://naver.com") .build(); mockMvc.perform( diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java index ac69ed93..30b5e5fb 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java @@ -197,7 +197,7 @@ void loginUpdateUserInfoWhenUserInfoChanged() { @Test @DisplayName("UNAUTH 미가입자 회원가입 성공 테스트") public void registerUnauthUserSuccessTest() { - UserPlatformType platformType = KAKAO; + UserPlatformType platformType =KAKAO; String email = "user@example.com"; String platformId = "qwer1234@"; String name = "tesrUser"; @@ -210,23 +210,25 @@ public void registerUnauthUserSuccessTest() { .platformType(platformType) .role(UserRole.UNAUTH) .email(email) + .name(name) + .profileImageUrl(profileImageUrl) .build(); User findUser = userRepository.save(unauthUser); // 회원가입 요청 생성 (CENTER) AuthServiceRegisterRequest request = AuthServiceRegisterRequest.builder() - .platformId(platformId) .role(UserRole.CENTER) - .profileImageUrl(profileImageUrl) .phoneNumber(phoneNumber) .email(email) - .name(name) .build(); // when - AuthServiceLoginResponse response = authService.register(platformType, request); - boolean tokenValid = jwtService.isTokenValid(response.getToken(), findUser); // 발행한 토큰 검증 + AuthServiceLoginResponse response = authService.register(request); + + User savedUser = userRepository.findByEmail(request.getEmail()).get(); + boolean tokenValid = jwtService.isTokenValid(response.getToken(), savedUser); // 발행한 토큰 검증 + // then assertEquals(UserRole.CENTER, response.getRole()); @@ -243,30 +245,29 @@ public void registerUnauthUserFailTest() { String phoneNumber = "01234567890"; String profileImageUrl = "https://example.com/profile.jpg"; - // UNAUTH 사용자 저장 - User unauthUser = User.builder() + User user = User.builder() .platformId(platformId) .platformType(platformType) - .role(UserRole.CENTER) // UNAUTH 미가입자가 아님 + .role(UserRole.CENTER) .email(email) + .name(name) + .profileImageUrl(profileImageUrl) .build(); - User findUser = userRepository.save(unauthUser); + + userRepository.save(user); // 회원가입 요청 생성 (CENTER) AuthServiceRegisterRequest request = AuthServiceRegisterRequest.builder() - .platformId(platformId) .role(UserRole.CENTER) - .profileImageUrl(profileImageUrl) .phoneNumber(phoneNumber) .email(email) - .name(name) .build(); // when // then assertThrows(RuntimeException.class, () -> { - authService.register(platformType, request); + authService.register(request); }); } } \ No newline at end of file diff --git a/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java b/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java index 2ae6461a..3d9b05da 100644 --- a/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java +++ b/heachi-domain-mysql/src/main/java/com/heachi/mysql/define/user/User.java @@ -46,19 +46,9 @@ public void updateProfile(String name, String profileImageUrl) { this.name = name; } - public void updateRegister(String platformId, - UserRole role, - String name, - String email, - String phoneNumber, - String profileImageUrl) { - this.platformId = platformId; + public void updateRegister(UserRole role, String phoneNumber) { this.role = role; - this.name = name; - this.email = email; this.phoneNumber = phoneNumber; - this.profileImageUrl = profileImageUrl; - } @Builder diff --git a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java index bacb83b3..64efe2ff 100644 --- a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java +++ b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java @@ -18,10 +18,9 @@ public enum ExceptionMessage { OAUTH_INVALID_TOKEN_URL("tokenURL이 정확하지 않습니다."), OAUTH_INVALID_ACCESS_TOKEN("잘못된 access_token 입니다."), - // RegisterException - OAUTH_INVALID_REGISTER("잘못된 회원가입 요청입니다."), - OAUTH_UNAUTH_DUPLICATE_REGISTER("UNAUTH인 사용자만 회원가입이 가능합니다."); - + // AuthException + AUTH_INVALID_REGISTER("잘못된 회원가입 요청입니다."), + AUTH_DUPLICATE_UNAUTH_REGISTER("중복된 회원가입 요청입니다."); private final String text; } diff --git a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/auth/AuthException.java b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/auth/AuthException.java new file mode 100644 index 00000000..1dce1685 --- /dev/null +++ b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/auth/AuthException.java @@ -0,0 +1,11 @@ +package com.heachi.admin.common.exception.auth; + +import com.heachi.admin.common.exception.ExceptionMessage; +import com.heachi.admin.common.exception.HeachiException; + +public class AuthException extends HeachiException { + + public AuthException(ExceptionMessage message) { + super(message.getText()); + } +} From ae3cfe70a4f592addba1ea3688f5f7feb686f56c Mon Sep 17 00:00:00 2001 From: jusung-c Date: Sun, 27 Aug 2023 21:28:08 +0900 Subject: [PATCH 10/27] =?UTF-8?q?refactor(#38):=20swagger=20=EC=A0=91?= =?UTF-8?q?=EC=86=8D=20=EC=95=88=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SecurityConfig 설정 수정 --- .../java/com/heachi/auth/config/security/SecurityConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java index c055536d..185c9e31 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/security/SecurityConfig.java @@ -27,9 +27,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti authorizeHttpRequests // UnAuth Area .requestMatchers("/auth/**").permitAll() - .requestMatchers("/auth/**/register").hasAnyAuthority("UNAUTH") // Swagger 3.0 .requestMatchers("/v3/api-docs/**", "/swagger-ui/**").permitAll() + // register + .requestMatchers("/auth/register").hasAnyAuthority("UNAUTH") // Others .anyRequest().hasAnyAuthority("USER", "CHEMIST", "CENTER") ) From 44d0fff9e8d4936a61f0b473c6b798d47d39c12f Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Mon, 28 Aug 2023 09:58:54 +0900 Subject: [PATCH 11/27] =?UTF-8?q?refactor(#36):=20Response=20DTO=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/auth/AuthController.java | 13 +++++- .../auth/response/UserSimpleInfoResponse.java | 41 +++++++++++++++++++ .../common/exception/ExceptionMessage.java | 1 + 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java index 9ad95103..e693607b 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java @@ -1,13 +1,17 @@ package com.heachi.auth.api.controller.auth; import com.heachi.admin.common.exception.ExceptionMessage; +import com.heachi.admin.common.exception.auth.AuthException; import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.admin.common.response.JsonResult; import com.heachi.auth.api.controller.auth.request.AuthRegisterRequest; +import com.heachi.auth.api.controller.auth.response.UserSimpleInfoResponse; import com.heachi.auth.api.service.auth.AuthService; import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse; import com.heachi.auth.api.service.oauth.OAuthService; +import com.heachi.mysql.define.user.User; import com.heachi.mysql.define.user.constant.UserPlatformType; +import com.heachi.mysql.define.user.constant.UserRole; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,6 +21,8 @@ import java.security.Principal; +import static com.heachi.mysql.define.user.constant.UserRole.*; + @Slf4j @RequiredArgsConstructor @RequestMapping("/auth") @@ -61,8 +67,11 @@ public JsonResult register( } @GetMapping("/info") - public JsonResult userInfo(@AuthenticationPrincipal UserDetails user) { + public JsonResult userInfo(@AuthenticationPrincipal User user) { + if (user.getRole() == UNAUTH) { + throw new AuthException(ExceptionMessage.AUTH_UNAUTHORIZED); + } - return JsonResult.successOf(user); + return JsonResult.successOf(UserSimpleInfoResponse.of(user)); } } diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java new file mode 100644 index 00000000..e66d9519 --- /dev/null +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java @@ -0,0 +1,41 @@ +package com.heachi.auth.api.controller.auth.response; + +import com.heachi.mysql.define.user.User; +import com.heachi.mysql.define.user.constant.UserPlatformType; +import com.heachi.mysql.define.user.constant.UserRole; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class UserSimpleInfoResponse { + private Long id; + private String platformId; + private UserPlatformType platformType; + private UserRole role; + private String name; + private String email; + private String profileImageUrl; + + @Builder + private UserSimpleInfoResponse(Long id, String platformId, UserPlatformType platformType, UserRole role, String name, String email, String profileImageUrl) { + this.id = id; + this.platformId = platformId; + this.platformType = platformType; + this.role = role; + this.name = name; + this.email = email; + this.profileImageUrl = profileImageUrl; + } + + public static UserSimpleInfoResponse of(User user) { + return UserSimpleInfoResponse.builder() + .id(user.getId()) + .platformId(user.getPlatformId()) + .platformType(user.getPlatformType()) + .role(user.getRole()) + .name(user.getName()) + .email(user.getEmail()) + .profileImageUrl(user.getProfileImageUrl()) + .build(); + } +} diff --git a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java index 1d09afbe..2f04fb12 100644 --- a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java +++ b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java @@ -21,6 +21,7 @@ public enum ExceptionMessage { // AuthException AUTH_SERVER_NOT_RESPOND("인증 서버가 응답하지 않습니다."), + AUTH_UNAUTHORIZED("현재 권한으로 실행할 수 없는 요청입니다."), ; From 2a2a5896593a9c47d8132951115681981928ac01 Mon Sep 17 00:00:00 2001 From: jusung-c Date: Tue, 29 Aug 2023 01:24:49 +0900 Subject: [PATCH 12/27] =?UTF-8?q?refactor(#38):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20try-catch=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 5 ++- .../auth/api/service/auth/AuthService.java | 45 ++++++++----------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/gradle.properties b/gradle.properties index c0d165dc..b303f660 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,4 +8,7 @@ projectGroup=com.heachi kotlinVersion=1.6.21 springBootVersion=3.1.2 springDependencyManagementVersion=1.1.2 -springCloudDependenciesVersion=2022.0.3 \ No newline at end of file +springCloudDependenciesVersion=2022.0.3 + +DOCKER_USERNAME= +DOCKER_PASSWORD= diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java index 861aa9c8..0d0582c2 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/auth/AuthService.java @@ -75,33 +75,26 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code @Transactional public AuthServiceLoginResponse register(AuthServiceRegisterRequest request) { - try { - User findUser = userRepository.findByEmail(request.getEmail()).orElseThrow(() -> { - // UNAUTH인 토큰을 받고 회원 탈퇴 후 그 토큰으로 회원가입 요청시 예외 처리 - throw new AuthException(ExceptionMessage.AUTH_INVALID_REGISTER); - }); - - // UNAUTH 토큰으로 회원가입을 요청했지만 이미 update되어 UNAUTH가 아닌 사용자 예외 처리 - if (findUser.getRole() != UserRole.UNAUTH) { - throw new AuthException(ExceptionMessage.AUTH_DUPLICATE_UNAUTH_REGISTER); - } - - // 회원가입 정보 DB 반영 - findUser.updateRegister(request.getRole(), request.getPhoneNumber()); - - // JWT 토큰 재발급 - final String token = createJwtToken(findUser); - - return AuthServiceLoginResponse.builder() - .token(token) - .role(findUser.getRole()) - .build(); - - } catch (OAuthException e) { - throw new RuntimeException(e.getMessage()); - } catch (Exception e) { - throw new RuntimeException("exception!!!"); + User findUser = userRepository.findByEmail(request.getEmail()).orElseThrow(() -> { + // UNAUTH인 토큰을 받고 회원 탈퇴 후 그 토큰으로 회원가입 요청시 예외 처리 + throw new AuthException(ExceptionMessage.AUTH_INVALID_REGISTER); + }); + + // UNAUTH 토큰으로 회원가입을 요청했지만 이미 update되어 UNAUTH가 아닌 사용자 예외 처리 + if (findUser.getRole() != UserRole.UNAUTH) { + throw new AuthException(ExceptionMessage.AUTH_DUPLICATE_UNAUTH_REGISTER); } + + // 회원가입 정보 DB 반영 + findUser.updateRegister(request.getRole(), request.getPhoneNumber()); + + // JWT 토큰 재발급 + final String token = createJwtToken(findUser); + + return AuthServiceLoginResponse.builder() + .token(token) + .role(findUser.getRole()) + .build(); } private String createJwtToken(User user) { From 14d09d22b655c834e4213e86f419c08121c91c16 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Wed, 13 Sep 2023 12:42:41 +0900 Subject: [PATCH 13/27] =?UTF-8?q?feat(#41):=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EC=95=8C=EB=A6=BC=20=ED=99=95=EC=9D=B8=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 5 +++- .../heachi/mongo/define/notify/Notify.java | 5 ++-- .../api/controller/NotifyController.java | 18 +++++++++++++ .../api/service/notify/NotifyService.java | 20 +++++++++++---- .../NotifyServiceReceiverResponse.java | 25 +++++++++++-------- .../common/exception/ExceptionMessage.java | 2 ++ .../exception/notify/NotifyException.java | 10 ++++++++ 7 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 heachi-support/common/src/main/java/com/heachi/admin/common/exception/notify/NotifyException.java diff --git a/gradle.properties b/gradle.properties index c0d165dc..48354356 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,4 +8,7 @@ projectGroup=com.heachi kotlinVersion=1.6.21 springBootVersion=3.1.2 springDependencyManagementVersion=1.1.2 -springCloudDependenciesVersion=2022.0.3 \ No newline at end of file +springCloudDependenciesVersion=2022.0.3 + +DOCKER_USERNAME= +DOCKER_PASSWORD= \ No newline at end of file diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java index 2be83046..d2593f6e 100644 --- a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java @@ -41,7 +41,8 @@ private Notify(String sendUserId, List receiveUserIds, NotifyType type, this.checked = checked; } - public void receiverUserCheckedNotify() { - + public void receiverUserCheckedNotify(String receiverUserId) { + checked.add(receiverUserId); + checkedTime.put(receiverUserId, LocalDateTime.now()); } } diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java b/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java index 203f86f8..f17b6740 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java @@ -27,6 +27,9 @@ public class NotifyController { private final NotifyService notifyService; private final AuthService authService; + /** + * 알림 구독하기 (Server Sent Event 방식) + */ @GetMapping(value = "/", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux receive(@RequestHeader(value = "Authorization", required = false, defaultValue = "token") String headers) { @@ -35,6 +38,9 @@ public Flux receive(@RequestHeader(value = "Authorization", required .subscribeOn(Schedulers.boundedElastic()); // publisher의 스케줄러를 boundedElastic으로 변경 } + /** + * 알림 추가하기 + */ @PostMapping("/") public Mono registNotify( @RequestHeader(value = "Authorization", required = false, defaultValue = "token") String headers, @@ -45,4 +51,16 @@ public Mono registNotify( .thenReturn(JsonResult.successOf())) .subscribeOn(Schedulers.boundedElastic()); // publisher의 스케줄러를 boundedElastic으로 변경 } + + /** + * 사용자 알림 읽기 이벤트 + */ + @GetMapping("/read/{notifyId}") + public Mono readNotify( + @RequestHeader(value = "Authorization", required = false, defaultValue = "token") String headers, + @PathVariable("notifyId") String notifyId) { + return authService.getUserId(headers) + .flatMap(userId -> notifyService.readNotify(userId, notifyId)) + .subscribeOn(Schedulers.boundedElastic()); + } } diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java index 3f022a7f..8e6479f4 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java @@ -1,5 +1,7 @@ package com.heachi.notify.api.service.notify; +import com.heachi.admin.common.exception.ExceptionMessage; +import com.heachi.admin.common.exception.notify.NotifyException; import com.heachi.admin.common.response.JsonResult; import com.heachi.mongo.define.notify.Notify; import com.heachi.mongo.define.notify.repository.NotifyRepository; @@ -7,6 +9,7 @@ import com.heachi.notify.api.service.notify.response.NotifyServiceReceiverResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -22,11 +25,8 @@ public class NotifyService { public Flux receive(String receiverId) { return notifyRepository.findByReceiveUserIds(receiverId) - .flatMap(notify -> { - NotifyServiceReceiverResponse nsrr = NotifyServiceReceiverResponse.of(notify); - - return Flux.just(JsonResult.successOf(nsrr)); - }) + .doOnCancel(() -> System.out.println(">>>>>>>cancel!!!!!!!!!!")) + .flatMap(notify -> Flux.just(JsonResult.successOf(NotifyServiceReceiverResponse.of(notify, receiverId)))) .timeout(Duration.ofSeconds(30)) .onErrorReturn(TimeoutException.class, JsonResult.failOf("Timeout")); // 30초가 지나면 타임아웃 } @@ -34,4 +34,14 @@ public Flux receive(String receiverId) { public Mono registNotify(NotifyServiceRegistRequest request) { return notifyRepository.save(request.toEntity()); } + + public Mono readNotify(String userId, String notifyId) { + return notifyRepository.findById(notifyId) + .switchIfEmpty(Mono.error(new NotifyException(ExceptionMessage.NOTIFY_NOT_FOUND))) + .flatMap(notify -> { + notify.receiverUserCheckedNotify(userId); // 알림 확인 체크 + return notifyRepository.save(notify); + }) + .map(notify -> JsonResult.successOf(NotifyServiceReceiverResponse.of(notify, userId))); + } } diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java index 8dc5ffdf..2b88438c 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java @@ -13,21 +13,23 @@ @Getter @ToString public class NotifyServiceReceiverResponse { + private String id; private String sendUserId; // 알림을 보낸 유저 아이디 - private List receiveUserIds = new ArrayList<>(); // 알림을 받는 아이디 + private String receiveUserIds; // 알림을 받는 아이디 private NotifyType type; // 알림 종류 private String message; // 알림 내용 private String generatedUrl; // 알림의 근원지 private String url; // 알림 클릭 시 이동할 주소 - private LocalDateTime createdTime = LocalDateTime.now(); // 알림 발생 시간 - private Map checkedTime = new HashMap<>(); // 알림 확인 시간 - private Set checked = new HashSet<>(); // 알림을 확인했는지 안했는지 + private LocalDateTime createdTime; // 알림 발생 시간 + private LocalDateTime checkedTime; // 알림 확인 시간 + private boolean checked; // 알림을 확인했는지 안했는지 private String dateDistance; // 얼마나 지난 알림인지 @Builder - private NotifyServiceReceiverResponse(String sendUserId, List receiveUserIds, NotifyType type, String message - , String generatedUrl, String url, LocalDateTime createdTime, Map checkedTime - , Set checked, String dateDistance) { + public NotifyServiceReceiverResponse(String id, String sendUserId, String receiveUserIds, NotifyType type, + String message, String generatedUrl, String url, LocalDateTime createdTime, + LocalDateTime checkedTime, boolean checked, String dateDistance) { + this.id = id; this.sendUserId = sendUserId; this.receiveUserIds = receiveUserIds; this.type = type; @@ -40,17 +42,18 @@ private NotifyServiceReceiverResponse(String sendUserId, List receiveUse this.dateDistance = dateDistance; } - public static NotifyServiceReceiverResponse of(Notify notify) { + public static NotifyServiceReceiverResponse of(Notify notify, String receiveUserId) { return NotifyServiceReceiverResponse.builder() + .id(notify.getId()) .sendUserId(notify.getSendUserId()) - .receiveUserIds(notify.getReceiveUserIds()) + .receiveUserIds(receiveUserId) .type(notify.getType()) .message(notify.getMessage()) .generatedUrl(notify.getGeneratedUrl()) .url(notify.getUrl()) .createdTime(notify.getCreatedTime()) - .checkedTime(notify.getCheckedTime()) - .checked(notify.getChecked()) + .checkedTime(notify.getCheckedTime().get(receiveUserId)) + .checked(notify.getChecked().contains(receiveUserId)) .dateDistance(DateDistance.of(notify.getCreatedTime())) .build(); } diff --git a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java index 1d09afbe..c24a5ee7 100644 --- a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java +++ b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java @@ -22,6 +22,8 @@ public enum ExceptionMessage { // AuthException AUTH_SERVER_NOT_RESPOND("인증 서버가 응답하지 않습니다."), + // NotifyException + NOTIFY_NOT_FOUND("해당 아이디의 알림을 찾을 수 없습니다."), ; private final String text; diff --git a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/notify/NotifyException.java b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/notify/NotifyException.java new file mode 100644 index 00000000..8bc2edf1 --- /dev/null +++ b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/notify/NotifyException.java @@ -0,0 +1,10 @@ +package com.heachi.admin.common.exception.notify; + +import com.heachi.admin.common.exception.ExceptionMessage; +import com.heachi.admin.common.exception.HeachiException; + +public class NotifyException extends HeachiException { + public NotifyException(ExceptionMessage message) { + super(message.getText()); + } +} From 751c52dd9f1001b139a139bea6aa389fc0e60f24 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Wed, 13 Sep 2023 13:41:07 +0900 Subject: [PATCH 14/27] =?UTF-8?q?refactor(#44):=20JWT=EA=B0=80=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=EB=A5=BC=20?= =?UTF-8?q?=EA=B1=B0=EC=B9=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/request/AuthRegisterRequest.java | 3 ++ .../auth/api/service/jwt/JwtService.java | 22 +++++++++---- .../filter/JwtAuthenticationFilter.java | 33 +++++++++++-------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java index 5a9d88dd..614dfb2a 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/request/AuthRegisterRequest.java @@ -3,6 +3,7 @@ import com.heachi.mysql.define.user.constant.UserRole; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; @@ -17,9 +18,11 @@ @AllArgsConstructor public class AuthRegisterRequest { @NotEmpty + @Email private String email; @Enumerated(EnumType.STRING) + @NotNull private UserRole role; // 숫자 값이므로 null이 아니어야 하니까 @NotEmpty 대신 @NotNull 사용 -> 빈 문자열("") 허용 diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/jwt/JwtService.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/jwt/JwtService.java index f1533a0a..b3009703 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/jwt/JwtService.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/service/jwt/JwtService.java @@ -1,5 +1,8 @@ package com.heachi.auth.api.service.jwt; +import com.heachi.admin.common.exception.ExceptionMessage; +import com.heachi.admin.common.exception.jwt.JwtException; +import com.heachi.mysql.define.user.constant.UserRole; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; @@ -51,16 +54,23 @@ public String generateToken(Map extraClaims, UserDetails userDet .compact(); } - public boolean isTokenValid(String token, UserDetails userDetails) { + public boolean isTokenValid(String token, String username) { Claims claims = extractAllClaims(token); - if (!claims.containsKey("role")) return false; - if (!claims.containsKey("name")) return false; - if (!claims.containsKey("profileImageUrl")) return false; + try { + if (!claims.containsKey("role")) { + UserRole.valueOf(claims.get("role", String.class)); + return false; + } + if (!claims.containsKey("name")) return false; + if (!claims.containsKey("profileImageUrl")) return false; + } catch (RuntimeException e) { // covered for NullPointException, IllegalArgumentException + throw new JwtException(ExceptionMessage.JWT_ILLEGAL_ARGUMENT); + } - String username = claims.getSubject(); + String claimsSubject = claims.getSubject(); - return (username.equals(userDetails.getUsername())) && !isTokenExpired(token); + return (claimsSubject.equals(username)) && !isTokenExpired(token); } private boolean isTokenExpired(String token) { diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java index 7bbc4cc2..8bda4e51 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java @@ -5,6 +5,9 @@ import com.heachi.admin.common.exception.jwt.JwtException; import com.heachi.admin.common.response.JsonResult; import com.heachi.auth.api.service.jwt.JwtService; +import com.heachi.mysql.define.user.User; +import com.heachi.mysql.define.user.constant.UserRole; +import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.UnsupportedJwtException; @@ -53,22 +56,26 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse token = authHeader.substring(7); userEmail = jwtService.extractUsername(token); - // token의 claims에 userEmail이 있는데, 인증정보가 없는 경우 -> 토큰 생성해주기 - if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail); // 유저 이메일 있는지 확인 + // token의 claims에 userEmail이 있는지 체크, 토큰이 유효한지 체크 + if (userEmail != null && jwtService.isTokenValid(token, userEmail)) { + Claims claims = jwtService.extractAllClaims(token); + UserDetails userDetails = User.builder() + .email(userEmail) + .role(UserRole.valueOf(claims.get("role", String.class))) + .name(claims.get("name", String.class)) + .profileImageUrl(claims.get("profileImageUrl", String.class)) + .build(); - if (jwtService.isTokenValid(token, userDetails)) { - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - userDetails, - null, - userDetails.getAuthorities() - ); + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); - authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - // SecurityContext에 Authenticaiton 등록 - SecurityContextHolder.getContext().setAuthentication(authToken); - } + // SecurityContext에 Authenticaiton 등록 + SecurityContextHolder.getContext().setAuthentication(authToken); } filterChain.doFilter(request, response); } catch (ExpiredJwtException e){ From 6b94d04f6825c5bdb4414a25012e7ca61218f4f5 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Wed, 13 Sep 2023 13:41:20 +0900 Subject: [PATCH 15/27] =?UTF-8?q?test(#44):=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/auth/AuthControllerTest.java | 8 ++-- .../api/service/auth/AuthServiceTest.java | 4 +- .../auth/api/service/jwt/JwtServiceTest.java | 37 ++++++++++++++++--- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java index 3b35ad51..c40e98f6 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java @@ -171,7 +171,7 @@ void registerSuccessTest() throws Exception { .build(); mockMvc.perform( - post("/auth/KAKAO/register") + post("/auth/register") .contentType(MediaType.APPLICATION_JSON) .content(JsonMapper.builder().build().writeValueAsString(request))) .andExpect(status().isOk()) @@ -185,18 +185,18 @@ void registerFailTest() throws Exception { // given // 유효성 검사 실패하는 request AuthRegisterRequest request = AuthRegisterRequest.builder() - .role(null) + .role(UserRole.USER) // userRole null .email("1-203-102-3") // 잘못된 형식의 email .phoneNumber("01012341234") .build(); mockMvc.perform( - post("/auth/KAKAO/register") + post("/auth/register") .contentType(MediaType.APPLICATION_JSON) .content(JsonMapper.builder().build().writeValueAsString(request))) // .andExpect(status().isBadRequest()); .andExpect(status().isOk()) .andExpect(jsonPath("$.resCode").value(400)) - .andExpect(jsonPath("$.resMsg").value("email: 이메일 형식을 맞춰야합니다")); + .andExpect(jsonPath("$.resMsg").value("email: must be a well-formed email address")); } } \ No newline at end of file diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java index 30b5e5fb..6ad0a9fd 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/auth/AuthServiceTest.java @@ -79,7 +79,7 @@ void loginSuccess() throws Exception { AuthServiceLoginResponse login = authService.login(platformType, code, state); // 로그인 프로세스 User findUser = userRepository.findByEmail(email).get(); // 로그인한 사용자 찾기 - boolean tokenValid = jwtService.isTokenValid(login.getToken(), findUser); // 발행한 토큰 검증 + boolean tokenValid = jwtService.isTokenValid(login.getToken(), findUser.getUsername()); // 발행한 토큰 검증 // then assertThat(tokenValid).isTrue(); @@ -227,7 +227,7 @@ public void registerUnauthUserSuccessTest() { AuthServiceLoginResponse response = authService.register(request); User savedUser = userRepository.findByEmail(request.getEmail()).get(); - boolean tokenValid = jwtService.isTokenValid(response.getToken(), savedUser); // 발행한 토큰 검증 + boolean tokenValid = jwtService.isTokenValid(response.getToken(), savedUser.getUsername()); // 발행한 토큰 검증 // then diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/jwt/JwtServiceTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/jwt/JwtServiceTest.java index 6e5659fa..2f82dd2d 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/jwt/JwtServiceTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/service/jwt/JwtServiceTest.java @@ -1,5 +1,6 @@ package com.heachi.auth.api.service.jwt; +import com.heachi.admin.common.exception.jwt.JwtException; import com.heachi.auth.TestConfig; import com.heachi.mysql.define.user.User; import com.heachi.mysql.define.user.constant.UserRole; @@ -46,7 +47,7 @@ void isValidTokenMalformed() { User savedUser = userRepository.save(user); // when - assertThatThrownBy(() -> jwtService.isTokenValid(malformedToken, savedUser)) + assertThatThrownBy(() -> jwtService.isTokenValid(malformedToken, savedUser.getUsername())) .isInstanceOf(MalformedJwtException.class); // then } @@ -64,7 +65,7 @@ void tokenExpiredTest() { String expiredToken = jwtService.generateToken(new HashMap<>(), user, new Date()); // then - assertThatThrownBy(() -> jwtService.isTokenValid(expiredToken, savedUser)) + assertThatThrownBy(() -> jwtService.isTokenValid(expiredToken, savedUser.getUsername())) .isInstanceOf(ExpiredJwtException.class); } @@ -91,7 +92,7 @@ void provideToken() { // then System.out.println("token = " + token); - boolean result = jwtService.isTokenValid(token, savedUser); + boolean result = jwtService.isTokenValid(token, savedUser.getUsername()); assertThat(result).isTrue(); } @@ -118,7 +119,7 @@ void provideTokenIncludeClaims() { // then - boolean result = jwtService.isTokenValid(token, savedUser); + boolean result = jwtService.isTokenValid(token, savedUser.getUsername()); assertThat(result).isTrue(); assertAll( () -> assertThat(claims.getSubject()).isEqualTo("kimminsu@dankook.ac.kr"), @@ -150,7 +151,33 @@ void provideTokenClaimsOmitElement() { // then - boolean result = jwtService.isTokenValid(token, savedUser); + boolean result = jwtService.isTokenValid(token, savedUser.getUsername()); assertThat(result).isFalse(); } + + @Test + @DisplayName("존재하지 않는 UserRole을 넣었을때 오류 발생") + void notexistUserRoleException() { + // given + User user = User.builder() + .name("김민수") + .role(null) + .email("kimminsu@dankook.ac.kr") + .profileImageUrl("https://google.com") + .build(); + User savedUser = userRepository.save(user); + + HashMap map = new HashMap<>(); + map.put("role", null); + map.put("name", savedUser.getName()); + map.put("profileImageUrl", savedUser.getProfileImageUrl()); + String token = jwtService.generateToken(map, savedUser); + + // when + assertThatThrownBy(() -> jwtService.isTokenValid(token, savedUser.getUsername())) + // then + .isInstanceOf(JwtException.class); + + + } } \ No newline at end of file From e2e2f5190530945d6711c57a09268c05fd5f1fc3 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Wed, 13 Sep 2023 14:11:51 +0900 Subject: [PATCH 16/27] =?UTF-8?q?delete(#44):=20state=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C=20(=EC=B6=94?= =?UTF-8?q?=ED=9B=84=20redis=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EC=98=88?= =?UTF-8?q?=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/api/controller/auth/AuthController.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java index 89760c58..0b11cbd3 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/AuthController.java @@ -48,12 +48,7 @@ public JsonResult login( @RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request) { - String requestState = request.getSession().getId(); - - // state 값 유효성 검증 - if (!state.equals(requestState)) { - throw new OAuthException(ExceptionMessage.OAUTH_INVALID_STATE); - } + // state 값 검증 (redis) AuthServiceLoginResponse loginResponse = authService.login(platformType, code, request.getSession().getId()); @@ -71,9 +66,6 @@ public JsonResult register( @GetMapping("/info") public JsonResult userInfo(@AuthenticationPrincipal User user) { - if (user.getRole() == UNAUTH) { - throw new AuthException(ExceptionMessage.AUTH_UNAUTHORIZED); - } return JsonResult.successOf(UserSimpleInfoResponse.of(user)); } From 444971c7e02f2efc12b3289ae5012cc802b4db09 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Wed, 13 Sep 2023 14:12:24 +0900 Subject: [PATCH 17/27] =?UTF-8?q?fix(#44):=20Filter=20contentType=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/heachi/auth/config/filter/JwtAuthenticationFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java index 8bda4e51..d3791147 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/config/filter/JwtAuthenticationFilter.java @@ -99,7 +99,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse private void jwtExceptionHandler(HttpServletResponse response, ExceptionMessage message) throws IOException { response.setStatus(HttpStatus.OK.value()); response.setCharacterEncoding("utf-8"); - response.setCharacterEncoding(MediaType.APPLICATION_JSON_VALUE); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.getWriter().write(objectMapper.writeValueAsString(JsonResult.failOf(message.getText()))); } } From e64a33e29beb48500a470904fb9a5db5b824376a Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Wed, 13 Sep 2023 14:13:03 +0900 Subject: [PATCH 18/27] =?UTF-8?q?refactor(#44):=20jwt=20Claims=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=EC=97=90=20=EB=93=A4=EC=96=B4=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EA=B0=92=EC=9C=BC=EB=A1=9C=20Dto=20=EB=A9=A4=EB=B2=84=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/response/UserSimpleInfoResponse.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java index e66d9519..aba647b6 100644 --- a/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java +++ b/heachi-core/auth-api/src/main/java/com/heachi/auth/api/controller/auth/response/UserSimpleInfoResponse.java @@ -1,26 +1,19 @@ package com.heachi.auth.api.controller.auth.response; import com.heachi.mysql.define.user.User; -import com.heachi.mysql.define.user.constant.UserPlatformType; import com.heachi.mysql.define.user.constant.UserRole; import lombok.Builder; import lombok.Getter; @Getter public class UserSimpleInfoResponse { - private Long id; - private String platformId; - private UserPlatformType platformType; private UserRole role; private String name; private String email; private String profileImageUrl; @Builder - private UserSimpleInfoResponse(Long id, String platformId, UserPlatformType platformType, UserRole role, String name, String email, String profileImageUrl) { - this.id = id; - this.platformId = platformId; - this.platformType = platformType; + private UserSimpleInfoResponse(UserRole role, String name, String email, String profileImageUrl) { this.role = role; this.name = name; this.email = email; @@ -29,9 +22,6 @@ private UserSimpleInfoResponse(Long id, String platformId, UserPlatformType plat public static UserSimpleInfoResponse of(User user) { return UserSimpleInfoResponse.builder() - .id(user.getId()) - .platformId(user.getPlatformId()) - .platformType(user.getPlatformType()) .role(user.getRole()) .name(user.getName()) .email(user.getEmail()) From dba4e0c9a9393088e80a7068f12168e9971bac16 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Wed, 13 Sep 2023 14:13:26 +0900 Subject: [PATCH 19/27] =?UTF-8?q?test(#44):=20Simple=20UserInfo=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/auth/AuthControllerTest.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java index c40e98f6..730a26d4 100644 --- a/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java +++ b/heachi-core/auth-api/src/test/java/com/heachi/auth/api/controller/auth/AuthControllerTest.java @@ -7,8 +7,10 @@ import com.heachi.auth.api.controller.auth.request.AuthRegisterRequest; import com.heachi.auth.api.service.auth.AuthService; import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest; +import com.heachi.auth.api.service.jwt.JwtService; import com.heachi.auth.api.service.oauth.OAuthService; import com.heachi.auth.api.service.oauth.response.OAuthResponse; +import com.heachi.mysql.define.user.User; import com.heachi.mysql.define.user.constant.UserPlatformType; import com.heachi.mysql.define.user.constant.UserRole; import com.heachi.mysql.define.user.repository.UserRepository; @@ -22,6 +24,8 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; +import java.util.HashMap; + import static com.heachi.mysql.define.user.constant.UserPlatformType.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.BDDMockito.given; @@ -46,6 +50,9 @@ class AuthControllerTest extends TestConfig { @Autowired private UserRepository userRepository; + @Autowired + private JwtService jwtService; + @AfterEach void tearDown() { userRepository.deleteAllInBatch(); @@ -199,4 +206,50 @@ void registerFailTest() throws Exception { .andExpect(jsonPath("$.resCode").value(400)) .andExpect(jsonPath("$.resMsg").value("email: must be a well-formed email address")); } + + @Test + @DisplayName("userSimpleInfo 테스트, role, name, email, profileImageUrl이 리턴된다.") + void userSimpleInfoResponseTest() throws Exception { + // given + User user = User.builder() + .name("김민수") + .role(UserRole.USER) + .email("kimminsu@dankook.ac.kr") + .profileImageUrl("https://google.com") + .build(); + User savedUser = userRepository.save(user); + + HashMap map = new HashMap<>(); + map.put("role", savedUser.getRole().name()); + map.put("name", savedUser.getName()); + map.put("profileImageUrl", savedUser.getProfileImageUrl()); + String token = jwtService.generateToken(map, savedUser); + + // when + mockMvc.perform(get("/auth/info") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer " + token)) + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$.resCode").value(200)) + .andExpect(jsonPath("$.resObj.role").value("USER")) + .andExpect(jsonPath("$.resObj.name").value("김민수")) + .andExpect(jsonPath("$.resObj.email").value("kimminsu@dankook.ac.kr")) + .andExpect(jsonPath("$.resObj.profileImageUrl").value("https://google.com")); + } + + @Test + @DisplayName("userSimpleInfo 실패 테스트, 잘못된 Token을 넣으면 오류를 뱉는다.") + void userSimpleInfoTestWhenInvalidToken() throws Exception { + // given + String token = "strangeToken"; + + // when + mockMvc.perform(get("/auth/info") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer " + token)) + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$.resCode").value(400)); + } } \ No newline at end of file From ac324f6535515e3843ba1f2ed028d798dbeae3ce Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Thu, 14 Sep 2023 21:58:16 +0900 Subject: [PATCH 20/27] =?UTF-8?q?refactor(#41):=20update=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=97=90=20receiverUserId=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/heachi/mongo/define/notify/Notify.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java index d2593f6e..80673d5b 100644 --- a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/Notify.java @@ -4,6 +4,7 @@ import lombok.Builder; import lombok.Getter; import lombok.ToString; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @@ -13,6 +14,7 @@ @Getter @ToString @Document(collection = "notify") +@Slf4j public class Notify { @Id private String id; @@ -42,6 +44,16 @@ private Notify(String sendUserId, List receiveUserIds, NotifyType type, } public void receiverUserCheckedNotify(String receiverUserId) { + // receiver에 해당 사용자가 있는지 확인한다. + this.receiveUserIds.stream() + .filter(id -> id.equals(receiverUserId)) + .findFirst() + .orElseThrow(() -> { + log.error(">>>> ReceiverIds에 해당 사용자가 존재하지 않습니다 : {}", receiverUserId); + throw new IllegalArgumentException(); + }); + + // 있다면, 체크표시 checked.add(receiverUserId); checkedTime.put(receiverUserId, LocalDateTime.now()); } From 77472795b0ff584d14464517f235cc97ea44e482 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Thu, 14 Sep 2023 21:59:45 +0900 Subject: [PATCH 21/27] refactor(#41): ReactiveMongoRepository -> ReactiveMongoTemplate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ReactiveMongoTemplate을 이용한 페이지 네이션으로 변경 --- .../notify/repository/NotifyRepository.java | 5 +-- .../repository/NotifyRepositoryCustom.java | 10 ++++++ .../repository/NotifyRepositoryImpl.java | 34 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java create mode 100644 heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepository.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepository.java index 9f405c07..a7a64c33 100644 --- a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepository.java +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepository.java @@ -8,9 +8,6 @@ import reactor.core.publisher.Flux; @Repository -public interface NotifyRepository extends ReactiveMongoRepository { +public interface NotifyRepository extends ReactiveMongoRepository, NotifyRepositoryCustom { - @Tailable - @Query("{ receiveUserIds : ?0 }") - public Flux findByReceiveUserIds(String userIds); } diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java new file mode 100644 index 00000000..2b03dcce --- /dev/null +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.heachi.mongo.define.notify.repository; + +import com.heachi.mongo.define.notify.Notify; +import reactor.core.publisher.Flux; + +public interface NotifyRepositoryCustom { + + public Flux findNotifyByReceiveUserIdsPaging(String userIds, int page); + +} diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java new file mode 100644 index 00000000..5f3bcb26 --- /dev/null +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java @@ -0,0 +1,34 @@ +package com.heachi.mongo.define.notify.repository; + +import com.heachi.mongo.define.notify.Notify; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class NotifyRepositoryImpl implements NotifyRepositoryCustom { + + private final ReactiveMongoTemplate mongoTemplate; + + @Override + public Flux findNotifyByReceiveUserIdsPaging(String userIds, int page) { + return Mono.just(PageRequest.of(page, 10, Sort.by("createdTime").descending())) + .map(pageable -> { + Query query = new Query() + .with(pageable); + query.addCriteria(Criteria.where("receiveUserIds").in(List.of(userIds))); + + return query; + } + ).flatMapMany(query -> mongoTemplate.find(query, Notify.class, "notify")); + } +} From a3ddb5c21c18f4ca6c01379ce378bb887cdd1868 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Thu, 14 Sep 2023 22:00:50 +0900 Subject: [PATCH 22/27] =?UTF-8?q?delete(#41):=20MongoDB=20Cursor=EB=A5=BC?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=20=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/heachi/mongo/config/JpaConfig.java | 14 -- .../java/com/heachi/mongo/TestConnection.java | 40 ----- .../heachi/mongo/config/JpaConfigTest.java | 17 -- .../repository/NotifyRepositoryTest.java | 43 ++++-- .../api/service/notify/NotifyServiceTest.java | 145 +++++++++--------- 5 files changed, 101 insertions(+), 158 deletions(-) delete mode 100644 heachi-domain-mongo/src/test/java/com/heachi/mongo/TestConnection.java diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/config/JpaConfig.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/config/JpaConfig.java index b79aacbd..a8db286a 100644 --- a/heachi-domain-mongo/src/main/java/com/heachi/mongo/config/JpaConfig.java +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/config/JpaConfig.java @@ -19,18 +19,4 @@ @RequiredArgsConstructor public class JpaConfig { - private final ReactiveMongoTemplate reactiveMongoTemplate; - - // notify Collection Setting - @Bean - public void mongoDBinit() { - // Collection 초기 세팅을 위해 Notify 객체를 생성했다가 지움 - reactiveMongoTemplate.insert(Notify.builder().build()) - .flatMap(notify -> reactiveMongoTemplate.remove(notify) - .then(reactiveMongoTemplate.executeCommand("{ convertToCapped: 'notify', size: 8192 }")) - .then(reactiveMongoTemplate.executeCommand("{ collStats: 'notify' }")) - .doOnNext(stats -> System.out.println("stats = " + stats)) - ) - .subscribe(); - } } \ No newline at end of file diff --git a/heachi-domain-mongo/src/test/java/com/heachi/mongo/TestConnection.java b/heachi-domain-mongo/src/test/java/com/heachi/mongo/TestConnection.java deleted file mode 100644 index 9f8587e1..00000000 --- a/heachi-domain-mongo/src/test/java/com/heachi/mongo/TestConnection.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.heachi.mongo; - -import com.mongodb.reactivestreams.client.MongoClient; -import org.bson.Document; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import static org.assertj.core.api.Assertions.assertThat; - -@SpringBootTest -public class TestConnection extends TestConfig { - - @Autowired - private ReactiveMongoTemplate reactiveMongoTemplate; - - @Test - @DisplayName("MongoDB에 컬렉션을 고정된 크기로 고정시키기 위해 capped 설정을 해준다.") - void mongoDBcappedConfiguration() { - // given - reactiveMongoTemplate.executeCommand("{ convertToCapped: 'notify', size: 8192 }"); - - // when - reactiveMongoTemplate.executeCommand("{ collStats: 'notify' }") - // then - .as(StepVerifier::create) - .expectNextMatches(document -> { - assertThat(document.get("capped")).isEqualTo(true); - assertThat(document.get("totalSize")).isEqualTo(8192); - - return true; - }) - .verifyComplete(); - } -} diff --git a/heachi-domain-mongo/src/test/java/com/heachi/mongo/config/JpaConfigTest.java b/heachi-domain-mongo/src/test/java/com/heachi/mongo/config/JpaConfigTest.java index 562c1953..60224f6d 100644 --- a/heachi-domain-mongo/src/test/java/com/heachi/mongo/config/JpaConfigTest.java +++ b/heachi-domain-mongo/src/test/java/com/heachi/mongo/config/JpaConfigTest.java @@ -14,22 +14,5 @@ @SpringBootTest class JpaConfigTest extends TestConfig { - @Autowired - private ReactiveMongoTemplate reactiveMongoTemplate; - @Test - @DisplayName("Spring Applicatoin이 띄워질때, MongoDB capped 설정이 true가 된다.") - void mongoDBConfigurationInitialized() { - // when - reactiveMongoTemplate.executeCommand("{ collStats: 'notify' }") - // then - .as(StepVerifier::create) - .expectNextMatches(document -> { - assertThat(document.get("capped")).isEqualTo(true); - assertThat(document.get("totalSize")).isEqualTo(8192); - - return true; - }) - .verifyComplete(); - } } \ No newline at end of file diff --git a/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java b/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java index 506926dd..2b7e1cb6 100644 --- a/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java +++ b/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java @@ -2,10 +2,7 @@ import com.heachi.mongo.TestConfig; import com.heachi.mongo.define.notify.Notify; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import reactor.core.publisher.Flux; @@ -13,6 +10,9 @@ import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -26,9 +26,10 @@ class NotifyRepositoryTest extends TestConfig { @AfterEach void tearDown() { - notifyRepository.deleteAll(); + notifyRepository.deleteAll().subscribe(); } + @Test @DisplayName("Notify 객체를 만들어 저장할 수 있다.") void saveTest() { @@ -50,18 +51,30 @@ void saveTest() { } @Test - @Disabled - @DisplayName("ReceiveUserIds 리스트에 ReceiveUserId가 존재하면 해당 Notify를 반환한다.") - void findByReceiveUserIds() { + @DisplayName("ReceiveUserId를 넣고, 페이지를 넣으면 해당 페이지의 Notify가 10개가 나온다.") + void selectNotifyWhereReceiveUserIdLimit10Pagination() { // given - Notify notify = Notify.builder() - .sendUserId("홍찬희") - .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) - .build(); + List list = new ArrayList<>(); + for (int i=0; i<10; i++) { + Notify notify = Notify.builder() + .sendUserId("ghdcksgml") + .receiveUserIds(List.of("ghdcksgml1")) + .createdTime(LocalDateTime.now()) + .build(); + list.add(notify); + } + + + Flux flux1 = notifyRepository.saveAll(list); + Flux flux2 = notifyRepository.findNotifyByReceiveUserIdsPaging("ghdcksgml1", 0); + // when - Mono save = notifyRepository.save(notify); - Flux ghdcksgml1 = notifyRepository.findByReceiveUserIds("ghdcksgml1"); - assertThat(ghdcksgml1.collectList().block()).isEqualTo(notify.getSendUserId()); + StepVerifier.create(Flux.concat(flux1, flux2).log()) + // then + .expectSubscription() + .expectNextCount(10) + .expectNextCount(10) + .verifyComplete(); } } \ No newline at end of file diff --git a/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java b/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java index 0435964f..6ea2b9bf 100644 --- a/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java +++ b/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java @@ -45,79 +45,80 @@ void setUp() { notifyRepository.deleteAll().block(); } - @Test - @DisplayName("구독하면, 30초가 커넥션이 유지되고, 30초가 지나면 커넥션은 끊긴다.") - void connection30secondsWhenClientSubscribe() { - // given - - // when - StepVerifier.withVirtualTime(() -> notifyService.receive("2954438047")) - // then - .expectSubscription() // 구독이 시작되고 나서 - .expectNoEvent(Duration.ofSeconds(30)) // 30초간 아무런 응답이 없으면 - .expectNextMatches(jsonResult -> { // 에러를 뱉음 - assertThat(jsonResult.getResCode()).isEqualTo(400); - assertThat(jsonResult.getResMsg()).isEqualTo("Timeout"); - return true; - }) - .verifyComplete(); - } - - @Test - @DisplayName("구독 도중 객체가 추가되었을때 값이 곧바로 반영된다.") - void addNotifyWhenSubscribeOn() { - // given - Notify notify1 = Notify.builder() - .sendUserId("ghdcksgml1") - .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) - .type(NotifyType.NOTE) - .createdTime(LocalDateTime.now()) - .checkedTime(new HashMap<>()) - .checked(new HashSet<>()) - .build(); - Notify notify2 = Notify.builder() - .sendUserId("ghdcksgml2") - .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) - .type(NotifyType.NOTE) - .createdTime(LocalDateTime.now()) - .checkedTime(new HashMap<>()) - .checked(new HashSet<>()) - .build(); - Notify notify3 = Notify.builder() - .sendUserId("ghdcksgml3") - .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) - .type(NotifyType.NOTE) - .createdTime(LocalDateTime.now()) - .checkedTime(new HashMap<>()) - .checked(new HashSet<>()) - .build(); - - notifyRepository.save(notify1).block(); // 매칭되는 아이디가 없으면 Flux가 실행이 안되기 때문에 임의의 데이터를 저장. - - // when - StepVerifier.withVirtualTime(() -> { - Flux event = Mono.delay(Duration.ofSeconds(2)).thenMany(notifyRepository.saveAll(List.of(notify2, notify3))); - Flux flux = notifyService.receive("ghdcksgml1"); - - return Flux.merge(event, flux).log(); - }) - // then - .expectSubscription() - .expectNextCount(1) // 임의의 데이터 1개 - .expectNoEvent(Duration.ofSeconds(2)) // 2초뒤 2개 저장됨 - .expectNextCount(4) // repository.save()가 2번 호출됨, notifyService.receive()에서 2개의 데이터를 바로 받음 - .expectNoEvent(Duration.ofSeconds(30)) // idle 상태가 30초 동안 지속되면 - .expectNextMatches(o -> { - if (o instanceof JsonResult) { - assertThat(((JsonResult) o).getResCode()).isEqualTo(400); - - return true; - } - return false; - }) - .verifyComplete(); - } +// @Test +// @DisplayName("구독하면, 30초가 커넥션이 유지되고, 30초가 지나면 커넥션은 끊긴다.") +// void connection30secondsWhenClientSubscribe() { +// // given +// +// // when +// StepVerifier.withVirtualTime(() -> notifyService.receive("2954438047",0)) +// // then +// .expectSubscription() // 구독이 시작되고 나서 +// .expectNoEvent(Duration.ofSeconds(30)) // 30초간 아무런 응답이 없으면 +// .expectNextMatches(jsonResult -> { // 에러를 뱉음 +// assertThat(jsonResult.getResCode()).isEqualTo(400); +// assertThat(jsonResult.getResMsg()).isEqualTo("Timeout"); +// +// return true; +// }) +// .verifyComplete(); +// } +// +// @Test +// @DisplayName("구독 도중 객체가 추가되었을때 값이 곧바로 반영된다.") +// void addNotifyWhenSubscribeOn() { +// // given +// Notify notify1 = Notify.builder() +// .sendUserId("ghdcksgml1") +// .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) +// .type(NotifyType.NOTE) +// .createdTime(LocalDateTime.now()) +// .checkedTime(new HashMap<>()) +// .checked(new HashSet<>()) +// .build(); +// Notify notify2 = Notify.builder() +// .sendUserId("ghdcksgml2") +// .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) +// .type(NotifyType.NOTE) +// .createdTime(LocalDateTime.now()) +// .checkedTime(new HashMap<>()) +// .checked(new HashSet<>()) +// .build(); +// Notify notify3 = Notify.builder() +// .sendUserId("ghdcksgml3") +// .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) +// .type(NotifyType.NOTE) +// .createdTime(LocalDateTime.now()) +// .checkedTime(new HashMap<>()) +// .checked(new HashSet<>()) +// .build(); +// +// notifyRepository.save(notify1).block(); // 매칭되는 아이디가 없으면 Flux가 실행이 안되기 때문에 임의의 데이터를 저장. +// +// // when +// StepVerifier.withVirtualTime(() -> { +// Flux event = Mono.delay(Duration.ofSeconds(2)).thenMany(notifyRepository.saveAll(List.of(notify2, notify3))); +// Flux flux = notifyService.receive("ghdcksgml1",0); +// +// return Flux.merge(event, flux).log(); +// }) +// // then +// .expectSubscription() +// .expectNextCount(1) // 임의의 데이터 1개 +// .expectNoEvent(Duration.ofSeconds(2)) // 2초뒤 2개 저장됨 +// .expectNextCount(4) // repository.save()가 2번 호출됨, notifyService.receive()에서 2개의 데이터를 바로 받음 +// .expectNoEvent(Duration.ofSeconds(30)) // idle 상태가 30초 동안 지속되면 +// .expectNextMatches(o -> { +// if (o instanceof JsonResult) { +// assertThat(((JsonResult) o).getResCode()).isEqualTo(400); +// +// return true; +// } +// return false; +// }) +// .verifyComplete(); +// } @Test @DisplayName("올바른 데이터를 넣어주면, Notify에 저장되고, ID도 부여된다.") From 170166a3872d6b04fedc23d42bb425775cc38bca Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Thu, 14 Sep 2023 22:04:19 +0900 Subject: [PATCH 23/27] =?UTF-8?q?refactor(#41):=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/NotifyController.java | 22 +++++++++++++--- .../api/service/notify/NotifyService.java | 26 +++++++++++-------- .../common/exception/ExceptionMessage.java | 1 + 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java b/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java index f17b6740..da0ddfa2 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java @@ -1,6 +1,7 @@ package com.heachi.notify.api.controller; import com.heachi.admin.common.exception.ExceptionMessage; +import com.heachi.admin.common.exception.notify.NotifyException; import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.admin.common.response.JsonResult; import com.heachi.external.clients.auth.AuthClients; @@ -18,6 +19,7 @@ import java.net.ConnectException; import java.time.Duration; +import java.util.List; @RestController @RequestMapping("/notify") @@ -28,13 +30,15 @@ public class NotifyController { private final AuthService authService; /** - * 알림 구독하기 (Server Sent Event 방식) + * 알림 받기 */ @GetMapping(value = "/", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux receive(@RequestHeader(value = "Authorization", required = false, defaultValue = "token") String headers) { + public Flux receive( + @RequestHeader(value = "Authorization", required = false, defaultValue = "token") String headers, + @RequestParam(name = "page", defaultValue = "0") int page) { return authService.getUserId(headers) - .flatMapMany(sendUserId -> notifyService.receive(sendUserId)) + .flatMapMany(sendUserId -> notifyService.receive(sendUserId, page)) .subscribeOn(Schedulers.boundedElastic()); // publisher의 스케줄러를 boundedElastic으로 변경 } @@ -45,9 +49,16 @@ public Flux receive(@RequestHeader(value = "Authorization", required public Mono registNotify( @RequestHeader(value = "Authorization", required = false, defaultValue = "token") String headers, @RequestBody NotifyRegistRequest request) { + return authService.getUserId(headers) + .flatMap(sendUserId -> + request.getReceiveUserIds().stream().filter(receiverId -> receiverId.equals(sendUserId)) + .findFirst() + .map(id -> Mono.error(new NotifyException(ExceptionMessage.NOTIFY_DUPLICATE_ID))) + .orElseGet(() -> Mono.just(sendUserId)) + ) .flatMap(sendUserId -> notifyService - .registNotify(NotifyServiceRegistRequest.of(request, sendUserId)) + .registNotify(NotifyServiceRegistRequest.of(request, sendUserId.toString())) .thenReturn(JsonResult.successOf())) .subscribeOn(Schedulers.boundedElastic()); // publisher의 스케줄러를 boundedElastic으로 변경 } @@ -59,8 +70,11 @@ public Mono registNotify( public Mono readNotify( @RequestHeader(value = "Authorization", required = false, defaultValue = "token") String headers, @PathVariable("notifyId") String notifyId) { + return authService.getUserId(headers) .flatMap(userId -> notifyService.readNotify(userId, notifyId)) + .onErrorMap(throwable -> new NotifyException(ExceptionMessage.NOTIFY_NOT_FOUND)) + .map(notify -> JsonResult.successOf()) .subscribeOn(Schedulers.boundedElastic()); } } diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java index 8e6479f4..4a2f21dc 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java @@ -15,6 +15,8 @@ import reactor.core.scheduler.Schedulers; import java.time.Duration; +import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeoutException; @Service @@ -23,25 +25,27 @@ public class NotifyService { private final NotifyRepository notifyRepository; - public Flux receive(String receiverId) { - return notifyRepository.findByReceiveUserIds(receiverId) - .doOnCancel(() -> System.out.println(">>>>>>>cancel!!!!!!!!!!")) - .flatMap(notify -> Flux.just(JsonResult.successOf(NotifyServiceReceiverResponse.of(notify, receiverId)))) - .timeout(Duration.ofSeconds(30)) - .onErrorReturn(TimeoutException.class, JsonResult.failOf("Timeout")); // 30초가 지나면 타임아웃 + public Flux receive(String receiverId, int page) { + return notifyRepository.findNotifyByReceiveUserIdsPaging(receiverId, page) + .flatMap(notify -> Flux.just(JsonResult.successOf(NotifyServiceReceiverResponse.of(notify, receiverId)))); } public Mono registNotify(NotifyServiceRegistRequest request) { return notifyRepository.save(request.toEntity()); } - public Mono readNotify(String userId, String notifyId) { + public Mono readNotify(String userId, String notifyId) { return notifyRepository.findById(notifyId) .switchIfEmpty(Mono.error(new NotifyException(ExceptionMessage.NOTIFY_NOT_FOUND))) .flatMap(notify -> { - notify.receiverUserCheckedNotify(userId); // 알림 확인 체크 - return notifyRepository.save(notify); - }) - .map(notify -> JsonResult.successOf(NotifyServiceReceiverResponse.of(notify, userId))); + // 이미 알림 확인했는지 체크 + if (!notify.getChecked().contains(userId)) { + notify.receiverUserCheckedNotify(userId); // 알림 확인 체크 + + return notifyRepository.save(notify); + } else { + return Mono.just(notify); + } + }); } } diff --git a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java index c24a5ee7..a761342d 100644 --- a/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java +++ b/heachi-support/common/src/main/java/com/heachi/admin/common/exception/ExceptionMessage.java @@ -24,6 +24,7 @@ public enum ExceptionMessage { // NotifyException NOTIFY_NOT_FOUND("해당 아이디의 알림을 찾을 수 없습니다."), + NOTIFY_DUPLICATE_ID("SendUser와 ReceiveUser가 같습니다."), ; private final String text; From 109f134954e975f90fdba35698f547a8e5283e0e Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Thu, 14 Sep 2023 22:05:42 +0900 Subject: [PATCH 24/27] =?UTF-8?q?setting(#41):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20import=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/heachi/notify/HeachiNotifyApplication.java | 1 - .../com/heachi/notify/api/controller/NotifyController.java | 7 ------- .../com/heachi/notify/api/service/auth/AuthService.java | 5 ----- .../heachi/notify/api/service/notify/NotifyService.java | 7 ------- .../notify/response/NotifyServiceReceiverResponse.java | 1 - .../notify/config/advice/GlobalExceptionHandler.java | 2 -- 6 files changed, 23 deletions(-) diff --git a/heachi-notify/src/main/java/com/heachi/notify/HeachiNotifyApplication.java b/heachi-notify/src/main/java/com/heachi/notify/HeachiNotifyApplication.java index e9a3285d..f2c5e68a 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/HeachiNotifyApplication.java +++ b/heachi-notify/src/main/java/com/heachi/notify/HeachiNotifyApplication.java @@ -1,6 +1,5 @@ package com.heachi.notify; -import com.heachi.admin.common.utils.DateDistance; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.reactive.config.EnableWebFlux; diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java b/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java index da0ddfa2..381fb619 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/controller/NotifyController.java @@ -2,9 +2,7 @@ import com.heachi.admin.common.exception.ExceptionMessage; import com.heachi.admin.common.exception.notify.NotifyException; -import com.heachi.admin.common.exception.oauth.OAuthException; import com.heachi.admin.common.response.JsonResult; -import com.heachi.external.clients.auth.AuthClients; import com.heachi.notify.api.controller.request.NotifyRegistRequest; import com.heachi.notify.api.service.auth.AuthService; import com.heachi.notify.api.service.notify.request.NotifyServiceRegistRequest; @@ -12,15 +10,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; -import org.springframework.web.reactive.function.client.WebClientRequestException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; -import java.net.ConnectException; -import java.time.Duration; -import java.util.List; - @RestController @RequestMapping("/notify") @RequiredArgsConstructor diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/service/auth/AuthService.java b/heachi-notify/src/main/java/com/heachi/notify/api/service/auth/AuthService.java index 0a42c1fc..505c4836 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/service/auth/AuthService.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/service/auth/AuthService.java @@ -3,17 +3,12 @@ import com.heachi.admin.common.exception.ExceptionMessage; import com.heachi.admin.common.exception.auth.AuthException; import com.heachi.admin.common.exception.jwt.JwtException; -import com.heachi.admin.common.response.JsonResult; import com.heachi.external.clients.auth.AuthClients; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.web.reactive.function.client.WebClientRequestException; -import reactor.core.Exceptions; import reactor.core.publisher.Mono; -import java.net.ConnectException; - @Slf4j @Service @RequiredArgsConstructor diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java index 4a2f21dc..907ee2c8 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java @@ -9,15 +9,8 @@ import com.heachi.notify.api.service.notify.response.NotifyServiceReceiverResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; - -import java.time.Duration; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeoutException; @Service @RequiredArgsConstructor diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java index 2b88438c..c0446af0 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/response/NotifyServiceReceiverResponse.java @@ -8,7 +8,6 @@ import lombok.ToString; import java.time.LocalDateTime; -import java.util.*; @Getter @ToString diff --git a/heachi-notify/src/main/java/com/heachi/notify/config/advice/GlobalExceptionHandler.java b/heachi-notify/src/main/java/com/heachi/notify/config/advice/GlobalExceptionHandler.java index bd6395cb..22afef95 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/config/advice/GlobalExceptionHandler.java +++ b/heachi-notify/src/main/java/com/heachi/notify/config/advice/GlobalExceptionHandler.java @@ -1,10 +1,8 @@ package com.heachi.notify.config.advice; -import com.heachi.admin.common.exception.HeachiException; import com.heachi.admin.common.response.JsonResult; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import reactor.core.publisher.Mono; @RestControllerAdvice public class GlobalExceptionHandler { From c9ccb400017cf264fe2a18c71ac6c884cae014bf Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Fri, 15 Sep 2023 08:34:46 +0900 Subject: [PATCH 25/27] =?UTF-8?q?feat(#41):=20=ED=95=B4=EB=8B=B9=20notify?= =?UTF-8?q?=EC=9D=98=20receiveUserIds=EC=97=90=20userId=EA=B0=80=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=ED=95=98=EB=A9=B4=20Notify=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EC=BF=BC=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notify/repository/NotifyRepositoryCustom.java | 11 ++++++++++- .../notify/repository/NotifyRepositoryImpl.java | 15 +++++++++++---- .../notify/api/service/notify/NotifyService.java | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java index 2b03dcce..4d1699ca 100644 --- a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryCustom.java @@ -2,9 +2,18 @@ import com.heachi.mongo.define.notify.Notify; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; public interface NotifyRepositoryCustom { - public Flux findNotifyByReceiveUserIdsPaging(String userIds, int page); + /** + * userId가 받는 알림을 페이징한다. (ReceiveUserIds In userId) + */ + public Flux findNotifyByReceiveUserIdsPaging(String userId, int page); + + /** + * notifyId로 notify를 검색하는데, userId가 recevierUserIds에 존재하는지 + */ + public Mono findNotifyByIdWhereReceiveUserIdsIn(String userId, String notifyId); } diff --git a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java index 5f3bcb26..cc8425c1 100644 --- a/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java +++ b/heachi-domain-mongo/src/main/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryImpl.java @@ -11,8 +11,6 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.List; - @Component @RequiredArgsConstructor public class NotifyRepositoryImpl implements NotifyRepositoryCustom { @@ -20,15 +18,24 @@ public class NotifyRepositoryImpl implements NotifyRepositoryCustom { private final ReactiveMongoTemplate mongoTemplate; @Override - public Flux findNotifyByReceiveUserIdsPaging(String userIds, int page) { + public Flux findNotifyByReceiveUserIdsPaging(String userId, int page) { return Mono.just(PageRequest.of(page, 10, Sort.by("createdTime").descending())) .map(pageable -> { Query query = new Query() .with(pageable); - query.addCriteria(Criteria.where("receiveUserIds").in(List.of(userIds))); + query.addCriteria(Criteria.where("receiveUserIds").in(userId)); return query; } ).flatMapMany(query -> mongoTemplate.find(query, Notify.class, "notify")); } + + @Override + public Mono findNotifyByIdWhereReceiveUserIdsIn(String userId, String notifyId) { + return Mono.just(new Query().addCriteria(Criteria.where("id").is(notifyId) + .and("receiveUserIds").in(userId))) + .flatMap(query -> mongoTemplate.findOne(query, Notify.class, "notify")); + } + + } diff --git a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java index 907ee2c8..84dd0d20 100644 --- a/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java +++ b/heachi-notify/src/main/java/com/heachi/notify/api/service/notify/NotifyService.java @@ -28,7 +28,7 @@ public Mono registNotify(NotifyServiceRegistRequest request) { } public Mono readNotify(String userId, String notifyId) { - return notifyRepository.findById(notifyId) + return notifyRepository.findNotifyByIdWhereReceiveUserIdsIn(userId, notifyId) .switchIfEmpty(Mono.error(new NotifyException(ExceptionMessage.NOTIFY_NOT_FOUND))) .flatMap(notify -> { // 이미 알림 확인했는지 체크 From f5e0ae08978a26003056dc9c83daaf567734523e Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Fri, 15 Sep 2023 08:35:02 +0900 Subject: [PATCH 26/27] =?UTF-8?q?test(#41):=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/NotifyRepositoryTest.java | 40 +++++++++++++ .../api/service/notify/NotifyServiceTest.java | 60 ++++++++++++++----- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java b/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java index 2b7e1cb6..2a4e6673 100644 --- a/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java +++ b/heachi-domain-mongo/src/test/java/com/heachi/mongo/define/notify/repository/NotifyRepositoryTest.java @@ -77,4 +77,44 @@ void selectNotifyWhereReceiveUserIdLimit10Pagination() { .expectNextCount(10) .verifyComplete(); } + + @Test + @DisplayName("notifyId가 일치하고, receiverUserIds에 나의 요청하는 아이디가 있다면, Notify를 가져온다.") + void selectNotifyWhereNotifyIdAndReceiverUserIdsIn() { + // given + Notify notify = Notify.builder() + .sendUserId("ghdcksgml") + .receiveUserIds(List.of("ghdcksgml1")) + .createdTime(LocalDateTime.now()) + .build(); + + Notify savedNotify = notifyRepository.save(notify).block(); + + // when + StepVerifier.create(notifyRepository.findNotifyByIdWhereReceiveUserIdsIn("ghdcksgml1", savedNotify.getId())) + // then + .expectSubscription() + .expectNextCount(1) + .verifyComplete(); + } + + @Test + @DisplayName("notifyId가 일치하지만, receiverUserIds에 요청하는 아이디가 존재하지 않는다면, 권한이 없으므로 아무것도 리턴되지 않는다.") + void selectNotifyWhereNotifyIdAndReceiverUserIdsInNotMatching() { + // given + Notify notify = Notify.builder() + .sendUserId("ghdcksgml") + .receiveUserIds(List.of("ghdcksgml1")) + .createdTime(LocalDateTime.now()) + .build(); + + Notify savedNotify = notifyRepository.save(notify).block(); + + // when + StepVerifier.create(notifyRepository.findNotifyByIdWhereReceiveUserIdsIn("ghdcksgml", savedNotify.getId())) + // then + .expectSubscription() + .expectNextCount(0) + .verifyComplete(); + } } \ No newline at end of file diff --git a/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java b/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java index 6ea2b9bf..9fac77d3 100644 --- a/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java +++ b/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java @@ -1,35 +1,22 @@ package com.heachi.notify.api.service.notify; -import com.heachi.admin.common.response.JsonResult; import com.heachi.mongo.define.notify.Notify; import com.heachi.mongo.define.notify.constant.NotifyType; import com.heachi.mongo.define.notify.repository.NotifyRepository; import com.heachi.notify.TestConfig; -import com.heachi.notify.api.service.notify.NotifyService; import com.heachi.notify.api.service.notify.request.NotifyServiceRegistRequest; -import com.heachi.notify.api.service.notify.response.NotifyServiceReceiverResponse; import org.junit.jupiter.api.*; -import org.mockito.BDDMockito; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Scheduler; -import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; -import reactor.test.scheduler.VirtualTimeScheduler; -import java.time.Duration; import java.time.LocalDateTime; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; @SpringBootTest class NotifyServiceTest extends TestConfig { @@ -125,7 +112,7 @@ void setUp() { void registNotify() { // given NotifyServiceRegistRequest notify = NotifyServiceRegistRequest.builder() - .sendUserId("ghdcksgml1") + .sendUserId("ghdcksgml") .receiveUserIds(List.of("ghdcksgml1", "ghdcksgml2", "ghdcksgml3")) .type(NotifyType.NOTE) .build(); @@ -146,4 +133,49 @@ void registNotify() { }) .verifyComplete(); } + + @Test + @DisplayName("자신이 Receiver로 지정되어 있는 알림을 읽으면 Checked가 true가 된다.") + void notifyReadEvent() { + // given + Notify notify = Notify.builder() + .sendUserId("ghdcksgml") + .receiveUserIds(List.of("ghdcksgml1")) + .checked(new HashSet<>()) + .checkedTime(new HashMap<>()) + .createdTime(LocalDateTime.now()) + .build(); + Notify savedNotify = notifyRepository.save(notify).block(); + + Mono mono = notifyService.readNotify("ghdcksgml1", savedNotify.getId()); + + // when + StepVerifier.create(mono) + // then + .expectSubscription() + .assertNext(notify1 -> assertThat(notify1.getChecked()).contains("ghdcksgml1")) + .verifyComplete(); + } + + + @Test + @DisplayName("findNotifyByIdWhereReceiveUserIdsIn에서 찾은 값이 없다면, NOTIFY_NOT_FOUND 에러를 발생시킨다.") + void notifyReadEventFailWhenFindQueryResultIsNull() { + // given + Notify notify = Notify.builder() + .sendUserId("ghdcksgml") + .receiveUserIds(List.of("ghdcksgml1")) + .createdTime(LocalDateTime.now()) + .build(); + Notify savedNotify = notifyRepository.save(notify).block(); + + Mono mono = notifyService.readNotify("UnKnownUser", savedNotify.getId()); + + // when + StepVerifier.create(mono) + // then + .expectSubscription() + .expectError() + .verify(); + } } \ No newline at end of file From 4aa7735506388fb675f71e5ad364465dc4d98717 Mon Sep 17 00:00:00 2001 From: ghdcksgml1 Date: Fri, 15 Sep 2023 08:42:24 +0900 Subject: [PATCH 27/27] =?UTF-8?q?test(#41):=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/heachi/notify/api/service/notify/NotifyServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java b/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java index 9fac77d3..cd13e9ec 100644 --- a/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java +++ b/heachi-notify/src/test/java/com/heachi/notify/api/service/notify/NotifyServiceTest.java @@ -126,7 +126,7 @@ void registNotify() { .expectSubscription() .expectNextMatches(n -> { assertThat(n.getId()).isNotNull(); - assertThat(n.getSendUserId()).isEqualTo("ghdcksgml1"); + assertThat(n.getSendUserId()).isEqualTo("ghdcksgml"); assertThat(n.getType()).isEqualTo(NotifyType.NOTE); return true;