From b65d9a79819f05e8a6e174bb9bfce88c23d7ef66 Mon Sep 17 00:00:00 2001 From: ChoiDongKuen Date: Tue, 23 Jan 2024 01:04:58 +0900 Subject: [PATCH] release: 0.2.1 (#141) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 모임 삭제 기능 구현 (#102) * refactor: 기획 변경에 따른 회원 카드 등록 API 리팩토링 (#105) * refactor: sql 변경 (#104) * refactor: User 클래스 필드명 변경 (#104) * refactor: API 변경에 따른 코드 리팩토링 (#104) * refactor: 불필요한 getId() 제거 ( 피드백 반영 ) (#104) * feat: 유효성 검증 관련 에러 일괄 처리을 위한 메소드 추가 (#104) * test: API 변경에 따른 통합 테스트 수정 및 단위 테스트 구현 (#104) * refactor: UserMeGetResponse 필드 변경 (#104) * fix: CI 에러 수정 (#104) * refactor: 피드백 반영 (#104) * fix: CI 에러 수정 (#104) * refactor: JWT 에러 응답 리팩토링 및 인가 인증 예최 처리 코드 수정 (#103) * refactor: 기타 코드 리팩토링( 피드백 반영 ) (#101) * chore: JWT 관련 의존성 변경 (#101) * refactor: JWT 및 인증 관련 로직 리팩토링 (#101) * test: 테스트 코드 및 설정 관련 변경(#101) * refactor: ObjectMapper Autowired 로 주입 (#101) * refactor: AuthService 반환 타입 Optional -> User 변경 (#101) * fix: V6_create_users_interests.sql 추가 (#109) (#110) * refactor: 회원 카드 등록시 JWT 정보도 함께 반환하도록 수정 (#114) * chore : application.properties 에 jwt 관련 설정 값 추가 (#113) * refactor : userService 회원 카드 등록 로직 수정 (#113) * feat : UserRegisterResponse 필드 추가(#113) * test: API 변경에 따른 테스트 관련 코드 수정 (#113) * fix: 소셜 로그인 관련 500 에러 수정 및 OAuth 로직 일부 개선 (#112) * fix: OAuthLoginController @RestController 어노테이션 추가(나는 바보..) 및 favicon 관련 임시 컨트롤러 생성 (#111) * refactor : 설정 yml 리팩토링 (#111) * refactor : SecurityConfig 리팩토링 (#111) * refactor : 기타 OAuth 관련 로직 리팩토링(#111) * refactor : Cors 허용 주소 임시 전부 허용 (#111) * fix : SonarCloud 오류 수정 (#111) * fix : SonarCloud 오류 수정 (#111) * feat: 위치 기반 API 구현 및 테스트 (#108) * refactor: 기타 코드 리팩토링( 피드백 반영 ) (#101) * chore: JWT 관련 의존성 변경 (#101) * refactor: JWT 및 인증 관련 로직 리팩토링 (#101) * test: 테스트 코드 및 설정 관련 변경(#101) * refactor: ObjectMapper Autowired 로 주입 (#101) * refactor: AuthService 반환 타입 Optional -> User 변경 (#101) * refactor: application.properties redis.port 변경 (#91) * refactor: 기존 Redis 설정 리팩토링 및 추가 구현 (#91) * feat: 유저 위치 기반 관련 DTO 및 VO 구현 (#91) * feat: 유저 위치 기반 API 구현 (#91) * test: 유저 위치 기반 통합 테스트 관련 클래스 구현 및 테스트 (#91) * test: redis port 변경에 따른 테스트 수정 (#91) * fix: CI 에러 수정 (#91) * fix: sonarCloud 에러 수정 (#91) * refactor: 리뷰 반영 (#108) * build: Gatling 세팅 (#115) * feat: 부하테스트 툴 Gatling을 세팅한다 * feat: 예시 코드를 작성한다 * refactor: Sample 코드의 이름을 변경한다 * feat: 내 정보 조회에 Oauth 정보 추가 (#119) * feat: 내 정보 조회에 Oauth 정보 추가 * test: Oauth 정보 추가에 따른 테스트 변경 * fix: 모임 목록 조회 조건에 활동 지역 조건이 안걸리는 버그 수정 (#121) * feat: 회원 탈퇴 사유 저장 로직 추가 및 cors 관련 재설정 (#123) * refactor: SecurityConfig Cors 관련 전체 허용으로 변경 및 회원 카드 등록 API permitAll 로 변경 (#117) * refactor: 기존 엔티티 관련 리팩토링 (#117) * feat: WithdrawReason 생성 sql 추가 (#117) * feat: WithdrawReason 관련 도메인 구현 (#117) * feat: 회원 탈퇴 시 탈퇴 사유 기능 추가 (#117) * test: 회원 탈퇴 단위 테스트 (#117) * refactor: 회원 탈퇴 API POST 로 수정 (#117) * refactor: 회원탈퇴 관련 통합 테스트 수정 및 요청 DTO 수정 (#117) * refactor: meeting 엔티티명 다시 복구 - 모든 테이블에 `s` 붙이는 방향 논의 필요 (#117) * feat: withdraw_reason 테이블 이름 변경 (#117) * fix: CI 에러 해결 (#117) * fix: 모임 규격 변경 (#127) * refactor: 코드 포매팅 적용 (#130) * refactor: 유저조회, 자신조회, 유저들 조회 api응답에 친구 수 필드 추가 (#135) * refactor: 유저조회, 자신조회, 유저들 조회 api에 친구 수 필드를 추가한다 * refactor: 자기자신 조회 api응답의 중복 oAuthType 필드를 삭제한다 * fix: JWT 재발급 오류 해결 (#138) * fix: 토큰 재발급 에러 수정 (#132) * fix: 토큰 재발급 에러 수정 (#132) * test: 토큰 재발급 에러 수정에 따른 테스트 수정 (#132) * test: SecurityConfig permitAll url 중복 제거 (#132) * refactor: 틈틈 위치 기반 로직 수정 및 테스트 (#139) * refactor: redis 관련 클래스 리팩토링 (#124) * feat: 요청에 따른 100 m 이내 6명 조회 로직 구현 (#124) * refactor: 위치 기반 DTO, VO 관련 리팩토링 (#124) * refactor: 위치 기반 DTO, VO 관련 리팩토링 (#124) * test: RedisRepository 리팩토링 및 추가 구현 (#124) * test: 위치 기반 관련 테스트 수정 (#124) * fix: sonarCloud 에러 수정 (#124) --------- Co-authored-by: ddingmin Co-authored-by: xb205 <62425964+devxb@users.noreply.github.com> Co-authored-by: devxb --- .../teumteum/auth/service/AuthService.java | 3 +- .../net/teumteum/core/config/RedisConfig.java | 7 +- .../core/security/service/JwtService.java | 9 +- .../core/security/service/RedisService.java | 52 ++++++++- .../controller/TeumTeumController.java | 21 ++-- .../{UserData.java => UserLocation.java} | 5 +- .../domain/request/UserLocationRequest.java | 14 +-- .../response/UserAroundLocationsResponse.java | 16 +-- .../teum_teum/service/TeumTeumService.java | 110 ++++++------------ .../teumteum/integration/RedisRepository.java | 44 +++---- .../net/teumteum/integration/Repository.java | 2 +- .../integration/TeumTeumIntegrationTest.java | 22 +--- .../teum_teum/UserLocationFixture.java | 6 +- .../unit/auth/service/AuthServiceTest.java | 17 +-- 14 files changed, 161 insertions(+), 167 deletions(-) rename src/main/java/net/teumteum/teum_teum/domain/{UserData.java => UserLocation.java} (63%) diff --git a/src/main/java/net/teumteum/auth/service/AuthService.java b/src/main/java/net/teumteum/auth/service/AuthService.java index ee91cce1..a2530fb9 100644 --- a/src/main/java/net/teumteum/auth/service/AuthService.java +++ b/src/main/java/net/teumteum/auth/service/AuthService.java @@ -49,7 +49,6 @@ private void checkRefreshTokenMatch(User user, String refreshToken) { private TokenResponse issueNewToken(User user) { - return new TokenResponse(jwtService.createAccessToken(user.getOauth().getOauthId()), - jwtService.createRefreshToken()); + return jwtService.createServiceToken(user); } } diff --git a/src/main/java/net/teumteum/core/config/RedisConfig.java b/src/main/java/net/teumteum/core/config/RedisConfig.java index a5cd9ea2..8773e7bb 100644 --- a/src/main/java/net/teumteum/core/config/RedisConfig.java +++ b/src/main/java/net/teumteum/core/config/RedisConfig.java @@ -23,12 +23,11 @@ public RedisConnectionFactory redisConnectionFactory() { } @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(redisConnectionFactory); + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); - redisTemplate.setDefaultSerializer(new StringRedisSerializer()); return redisTemplate; } } diff --git a/src/main/java/net/teumteum/core/security/service/JwtService.java b/src/main/java/net/teumteum/core/security/service/JwtService.java index fda0e80d..eb939017 100644 --- a/src/main/java/net/teumteum/core/security/service/JwtService.java +++ b/src/main/java/net/teumteum/core/security/service/JwtService.java @@ -18,7 +18,6 @@ import net.teumteum.core.property.JwtProperty; import net.teumteum.user.domain.User; import org.springframework.beans.factory.InitializingBean; -import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; @@ -60,12 +59,14 @@ public String extractRefreshToken(HttpServletRequest request) { public Long getUserIdFromToken(String token) { try { - return Long.valueOf(getClaims(token).get("id", String.class)); - } catch (Exception exception) { - throw new JwtException("Access Token is not valid"); + Claims claims = getClaims(token); + return claims.get("id", Long.class); + } catch (ExpiredJwtException exception) { + return Long.valueOf(exception.getClaims().get("id").toString()); } } + public TokenResponse createServiceToken(User users) { String accessToken = createAccessToken(users.getId().toString()); String refreshToken = createRefreshToken(); diff --git a/src/main/java/net/teumteum/core/security/service/RedisService.java b/src/main/java/net/teumteum/core/security/service/RedisService.java index 323263ac..92234455 100644 --- a/src/main/java/net/teumteum/core/security/service/RedisService.java +++ b/src/main/java/net/teumteum/core/security/service/RedisService.java @@ -1,30 +1,72 @@ package net.teumteum.core.security.service; +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PostConstruct; +import java.io.IOException; import java.time.Duration; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import net.teumteum.teum_teum.domain.UserLocation; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class RedisService { - private final RedisTemplate redisTemplate; + + private static final String HASH_KEY = "userLocation"; + private final RedisTemplate redisTemplate; + private final ObjectMapper objectMapper; + private ValueOperations valueOperations; + + @PostConstruct + void init() { + valueOperations = redisTemplate.opsForValue(); + } public String getData(String key) { - return (String) redisTemplate.opsForValue().get(key); + return valueOperations.get(key); } public void setData(String key, String value) { - redisTemplate.opsForValue().set(key, value); + valueOperations.set(key, value); } public void setDataWithExpiration(String key, String value, Long duration) { Duration expireDuration = Duration.ofSeconds(duration); - redisTemplate.opsForValue().set(key, value, expireDuration); + valueOperations.set(key, value, expireDuration); } public void deleteData(String key) { - redisTemplate.delete(key); + valueOperations.getOperations().delete(key); + } + + public void setUserLocation(UserLocation userLocation, Long duration) { + String key = HASH_KEY + userLocation.id(); + String value; + try { + value = objectMapper.writeValueAsString(userLocation); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e); + } + valueOperations.set(key, value, duration); + } + + public Set getAllUserLocations() { + Set keys = redisTemplate.keys(HASH_KEY + ":*"); + return requireNonNull(keys).stream().map(key -> { + String value = valueOperations.get(key); + try { + return objectMapper.readValue(value, UserLocation.class); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + }).collect(Collectors.toSet()); } } diff --git a/src/main/java/net/teumteum/teum_teum/controller/TeumTeumController.java b/src/main/java/net/teumteum/teum_teum/controller/TeumTeumController.java index 203960bb..aa42561e 100644 --- a/src/main/java/net/teumteum/teum_teum/controller/TeumTeumController.java +++ b/src/main/java/net/teumteum/teum_teum/controller/TeumTeumController.java @@ -30,18 +30,19 @@ public class TeumTeumController { @ResponseStatus(HttpStatus.OK) public UserAroundLocationsResponse getUserAroundLocations( @Valid @RequestBody UserLocationRequest request) { - return teumTeumService.processingUserAroundLocations(request); + return teumTeumService.saveAndGetUserAroundLocations(request); } @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler(MethodArgumentNotValidException.class) - public ErrorResponse handleMethodArgumentNotValidException( - MethodArgumentNotValidException methodArgumentNotValidException) { - Sentry.captureException(methodArgumentNotValidException); - - BindingResult bindingResult = methodArgumentNotValidException.getBindingResult(); - List errors = bindingResult.getAllErrors(); - - return ErrorResponse.of(errors.get(0).getDefaultMessage()); + @ExceptionHandler({IllegalArgumentException.class, MethodArgumentNotValidException.class}) + public ErrorResponse handleException(Exception exception) { + Sentry.captureException(exception); + if (exception instanceof MethodArgumentNotValidException methodArgumentNotValidException) { + BindingResult bindingResult = methodArgumentNotValidException.getBindingResult(); + List errors = bindingResult.getAllErrors(); + return ErrorResponse.of(errors.get(0).getDefaultMessage()); + } else { + return ErrorResponse.of(exception); + } } } diff --git a/src/main/java/net/teumteum/teum_teum/domain/UserData.java b/src/main/java/net/teumteum/teum_teum/domain/UserLocation.java similarity index 63% rename from src/main/java/net/teumteum/teum_teum/domain/UserData.java rename to src/main/java/net/teumteum/teum_teum/domain/UserLocation.java index e6943563..939e8343 100644 --- a/src/main/java/net/teumteum/teum_teum/domain/UserData.java +++ b/src/main/java/net/teumteum/teum_teum/domain/UserLocation.java @@ -1,8 +1,9 @@ package net.teumteum.teum_teum.domain; - -public record UserData( +public record UserLocation( Long id, + Double latitude, + Double longitude, String name, String jobDetailClass, Long characterId diff --git a/src/main/java/net/teumteum/teum_teum/domain/request/UserLocationRequest.java b/src/main/java/net/teumteum/teum_teum/domain/request/UserLocationRequest.java index 12619f82..3862d51d 100644 --- a/src/main/java/net/teumteum/teum_teum/domain/request/UserLocationRequest.java +++ b/src/main/java/net/teumteum/teum_teum/domain/request/UserLocationRequest.java @@ -2,15 +2,15 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import net.teumteum.teum_teum.domain.UserData; +import net.teumteum.teum_teum.domain.UserLocation; public record UserLocationRequest( - @NotNull(message = "경도는 필수 입력값입니다.") - Double longitude, - @NotNull(message = "위도는 필수 입력값입니다.") - Double latitude, @NotNull(message = "id 는 필수 입력값입니다.") Long id, + @NotNull(message = "위도는 필수 입력값입니다.") + Double latitude, + @NotNull(message = "경도는 필수 입력값입니다.") + Double longitude, @NotBlank(message = "이름은 필수 입력값입니다.") String name, @NotBlank(message = "직무는 필수 입력값입니다.") @@ -19,7 +19,7 @@ public record UserLocationRequest( Long characterId ) { - public UserData toUserData() { - return new UserData(id, name, jobDetailClass, characterId); + public UserLocation toUserLocation() { + return new UserLocation(id, latitude, longitude, name, jobDetailClass, characterId); } } diff --git a/src/main/java/net/teumteum/teum_teum/domain/response/UserAroundLocationsResponse.java b/src/main/java/net/teumteum/teum_teum/domain/response/UserAroundLocationsResponse.java index db9363d2..0b093f63 100644 --- a/src/main/java/net/teumteum/teum_teum/domain/response/UserAroundLocationsResponse.java +++ b/src/main/java/net/teumteum/teum_teum/domain/response/UserAroundLocationsResponse.java @@ -1,13 +1,13 @@ package net.teumteum.teum_teum.domain.response; import java.util.List; -import net.teumteum.teum_teum.domain.UserData; +import net.teumteum.teum_teum.domain.UserLocation; public record UserAroundLocationsResponse( - List userLocations + List aroundUserLocations ) { - public static UserAroundLocationsResponse of(List userData) { + public static UserAroundLocationsResponse of(List userData) { return new UserAroundLocationsResponse( userData.stream() .map(UserAroundLocationResponse::of) @@ -23,13 +23,13 @@ public record UserAroundLocationResponse( ) { public static UserAroundLocationResponse of( - UserData userData + UserLocation userLocation ) { return new UserAroundLocationResponse( - userData.id(), - userData.name(), - userData.jobDetailClass(), - userData.characterId() + userLocation.id(), + userLocation.name(), + userLocation.jobDetailClass(), + userLocation.characterId() ); } } diff --git a/src/main/java/net/teumteum/teum_teum/service/TeumTeumService.java b/src/main/java/net/teumteum/teum_teum/service/TeumTeumService.java index 1963cd1c..16ef457e 100644 --- a/src/main/java/net/teumteum/teum_teum/service/TeumTeumService.java +++ b/src/main/java/net/teumteum/teum_teum/service/TeumTeumService.java @@ -1,29 +1,19 @@ package net.teumteum.teum_teum.service; -import static java.lang.System.currentTimeMillis; -import static java.time.Duration.ofMinutes; +import static java.lang.Math.atan2; +import static java.lang.Math.sin; +import static java.lang.Math.sqrt; +import static java.lang.Math.toRadians; +import static java.util.Comparator.comparingDouble; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.time.Duration; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; +import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.teumteum.teum_teum.domain.UserData; +import net.teumteum.core.security.service.RedisService; +import net.teumteum.teum_teum.domain.UserLocation; import net.teumteum.teum_teum.domain.request.UserLocationRequest; import net.teumteum.teum_teum.domain.response.UserAroundLocationsResponse; -import net.teumteum.teum_teum.domain.response.UserAroundLocationsResponse.UserAroundLocationResponse; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Point; -import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; -import org.springframework.data.redis.core.GeoOperations; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.domain.geo.Metrics; import org.springframework.stereotype.Service; @Slf4j @@ -31,73 +21,39 @@ @RequiredArgsConstructor public class TeumTeumService { - private static final String KEY = "userLocation"; private static final int SEARCH_LIMIT = 6; - private static final Duration LOCATION_EXPIRATION = ofMinutes(1); - private final ObjectMapper objectMapper; + private final RedisService redisService; - private final RedisTemplate redisTemplate; - - public UserAroundLocationsResponse processingUserAroundLocations(UserLocationRequest request) { - GeoOperations geoValueOperations = redisTemplate.opsForGeo(); - - String userDataJson = null; - try { - userDataJson = objectMapper.writeValueAsString( - request.toUserData()) + ":" + currentTimeMillis(); - } catch (JsonProcessingException e) { - log.error("JsonProcessingException Occurred!"); - } - - geoValueOperations.add(KEY, new Point(request.longitude(), request.latitude()), userDataJson); - - return getUserAroundLocations(geoValueOperations, request.longitude(), request.latitude()); + public UserAroundLocationsResponse saveAndGetUserAroundLocations(UserLocationRequest request) { + redisService.setUserLocation(request.toUserLocation(), 60L); + return getUserAroundLocations(request); } - private UserAroundLocationsResponse getUserAroundLocations(GeoOperations geoValueOperations, - Double longitude, Double latitude) { + private UserAroundLocationsResponse getUserAroundLocations(UserLocationRequest request) { + Set allUserLocations = redisService.getAllUserLocations(); - GeoResults> geoResults - = geoValueOperations.radius(KEY, - new Circle(new Point(longitude, latitude), new Distance(100, Metrics.METERS))); + List aroundUserLocations = allUserLocations.stream() + .filter(userLocation -> !userLocation.id().equals(request.id())) + .filter(userLocation -> calculateDistance(request.latitude(), request.longitude(), + userLocation.latitude(), userLocation.longitude()) <= 100) + .sorted(comparingDouble(userLocation + -> calculateDistance(request.latitude(), request.longitude(), + userLocation.latitude(), userLocation.longitude())) + ).limit(SEARCH_LIMIT) + .toList(); - return getUserAroundLocationsResponse(geoResults); + return UserAroundLocationsResponse.of(aroundUserLocations); } - private UserAroundLocationsResponse getUserAroundLocationsResponse(GeoResults> geoResults) { - - List userAroundLocationResponses = new ArrayList<>(); - - long currentTime = currentTimeMillis(); - int count = 0; - - for (GeoResult> geoResult : Objects.requireNonNull(geoResults)) { - String userSavedTime = String.valueOf(geoResult.getContent().getName()).split(":")[5]; - long timestamp = Long.parseLong(userSavedTime); - - if (currentTime - timestamp < LOCATION_EXPIRATION.toMillis()) { - String savedUserLocation = String.valueOf(geoResult.getContent().getName()); - String userDataJson = savedUserLocation.substring(savedUserLocation.lastIndexOf(":") + 1); - - UserData userData = null; - try { - userData = objectMapper.readValue(userDataJson, UserData.class); - } catch (JsonProcessingException e) { - log.error("JsonProcessingException Occurred!"); - } - - UserAroundLocationResponse userAroundLocationResponse - = UserAroundLocationResponse.of(Objects.requireNonNull(userData)); - - userAroundLocationResponses.add(userAroundLocationResponse); - count++; - - if (count >= SEARCH_LIMIT) { - break; - } - } - } - return new UserAroundLocationsResponse(userAroundLocationResponses); + private double calculateDistance(double latitude1, double longitude1, double latitude2, double longitude2) { + final int earthRadius = 6371; + double latDistance = toRadians(latitude2 - latitude1); + double lonDistance = toRadians(longitude2 - longitude1); + double a = sin(latDistance / 2) * sin(latDistance / 2) + + Math.cos(toRadians(latitude1)) * Math.cos(toRadians(latitude2)) + * sin(lonDistance / 2) * sin(lonDistance / 2); + double c = 2 * atan2(sqrt(a), sqrt(1 - a)); + return earthRadius * c * 1000; } } diff --git a/src/test/java/net/teumteum/integration/RedisRepository.java b/src/test/java/net/teumteum/integration/RedisRepository.java index 2660ed9d..5bce1590 100644 --- a/src/test/java/net/teumteum/integration/RedisRepository.java +++ b/src/test/java/net/teumteum/integration/RedisRepository.java @@ -1,41 +1,45 @@ package net.teumteum.integration; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import net.teumteum.core.security.service.RedisService; +import net.teumteum.teum_teum.domain.UserLocation; import org.springframework.boot.test.context.TestComponent; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Point; -import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; -import org.springframework.data.redis.core.GeoOperations; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; @TestComponent @RequiredArgsConstructor public class RedisRepository { + private static final String HASH_KEY = "userLocation"; private final RedisService redisService; - private final RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; + private final ObjectMapper objectMapper; + private ValueOperations valueOperations; - public void saveGeoRedisData(String key, Point point, String member) { - GeoOperations geoOperations = redisTemplate.opsForGeo(); - geoOperations.add(key, point, member); + @PostConstruct + void init() { + valueOperations = redisTemplate.opsForValue(); } - public GeoResults> getGeoRedisData(String key, Circle circle) { - GeoOperations geoOperations = redisTemplate.opsForGeo(); - return geoOperations.radius(key, circle); - } - public void saveRedisDataWithExpiration(String key, String value, Long duration) { - redisService.setDataWithExpiration(key, value, duration); + public void setUserLocation(UserLocation userLocation, Long duration) { + String key = HASH_KEY + userLocation.id(); + String value; + try { + value = objectMapper.writeValueAsString(userLocation); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e); + } + valueOperations.set(key, value, duration); } - void deleteRedisData(String key) { - redisService.deleteData(key); - } - public String getRedisData(String key) { - return redisService.getData(key); + public void saveRedisDataWithExpiration(String key, String value, Long duration) { + redisService.setDataWithExpiration(key, value, duration); } } + diff --git a/src/test/java/net/teumteum/integration/Repository.java b/src/test/java/net/teumteum/integration/Repository.java index a945b29c..f0e80375 100644 --- a/src/test/java/net/teumteum/integration/Repository.java +++ b/src/test/java/net/teumteum/integration/Repository.java @@ -28,7 +28,7 @@ public class Repository { private final RedisService redisService; - private final RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; public User saveAndGetUser() { var user = UserFixture.getNullIdUser(); diff --git a/src/test/java/net/teumteum/integration/TeumTeumIntegrationTest.java b/src/test/java/net/teumteum/integration/TeumTeumIntegrationTest.java index a207aaad..739d5344 100644 --- a/src/test/java/net/teumteum/integration/TeumTeumIntegrationTest.java +++ b/src/test/java/net/teumteum/integration/TeumTeumIntegrationTest.java @@ -2,16 +2,14 @@ import java.util.List; import net.teumteum.teum_teum.UserLocationFixture; -import net.teumteum.teum_teum.domain.UserData; +import net.teumteum.teum_teum.domain.UserLocation; import net.teumteum.teum_teum.domain.response.UserAroundLocationsResponse; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.data.geo.Point; import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException; -import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; @DisplayName("틈틈 서비스 통합테스트의") public class TeumTeumIntegrationTest extends IntegrationTest { @@ -22,22 +20,14 @@ class Save_user_location_logic { private static final String VALID_TOKEN = "VALID_TOKEN"; private static final String INVALID_TOKEN = "IN_VALID_TOKEN"; - private static final String KEY = "userLocation"; - private static final long currentTimeMillis = 2000L; @BeforeEach void init() throws JsonProcessingException { - Point point01 = new Point(120.3, -22.4); - Point point02 = new Point(120.4, -22.2); + UserLocation userLocation01 = new UserLocation(100L, 20.2, 20.2, "Selly", "frontend", 1L); + UserLocation userLocation02 = new UserLocation(101L, 20.2, 20.2, "John", "Design", 2L); - UserData userData01 = new UserData(100L, "Selly", "frontend", 1L); - UserData userData02 = new UserData(101L, "Tom", "UX design", 5L); - - redisRepository.saveGeoRedisData(KEY, point01, - new ObjectMapper().writeValueAsString(userData01 + ":" + currentTimeMillis)); - - redisRepository.saveGeoRedisData(KEY, point02, - new ObjectMapper().writeValueAsString(userData02 + ":" + currentTimeMillis)); + redisRepository.setUserLocation(userLocation01, 60L); + redisRepository.setUserLocation(userLocation02, 60L); } @Test @@ -55,7 +45,7 @@ void if_user_request_valid_save_successfully() { result.expectBody(UserAroundLocationsResponse.class).returnResult() .getResponseBody()) .usingRecursiveComparison() - .isNull(); + .isNotNull(); } } } diff --git a/src/test/java/net/teumteum/teum_teum/UserLocationFixture.java b/src/test/java/net/teumteum/teum_teum/UserLocationFixture.java index f896278e..522078b7 100644 --- a/src/test/java/net/teumteum/teum_teum/UserLocationFixture.java +++ b/src/test/java/net/teumteum/teum_teum/UserLocationFixture.java @@ -16,9 +16,9 @@ public static UserAroundLocationResponse getDefaultUserAroundLocationResponse() public static UserLocationRequest newUserLocationRequest(UserLocationBuilder userLocationBuilder) { return new UserLocationRequest( + userLocationBuilder.id, userLocationBuilder.longitude, userLocationBuilder.latitude, - userLocationBuilder.id, userLocationBuilder.name, userLocationBuilder.jobDetailClass, userLocationBuilder.characterId @@ -39,10 +39,10 @@ public static UserAroundLocationResponse newUserAroundLocationResponse( public static class UserLocationBuilder { @Builder.Default - private Double longitude = 120.5; + private Double longitude = 20.2; @Builder.Default - private Double latitude = -22.3; + private Double latitude = 20.2; @Builder.Default private Long id = 1L; diff --git a/src/test/java/net/teumteum/unit/auth/service/AuthServiceTest.java b/src/test/java/net/teumteum/unit/auth/service/AuthServiceTest.java index 5e194698..3163c935 100644 --- a/src/test/java/net/teumteum/unit/auth/service/AuthServiceTest.java +++ b/src/test/java/net/teumteum/unit/auth/service/AuthServiceTest.java @@ -1,6 +1,8 @@ package net.teumteum.unit.auth.service; import static net.teumteum.core.security.Authenticated.네이버; +import static net.teumteum.unit.auth.common.SecurityValue.INVALID_ACCESS_TOKEN; +import static net.teumteum.unit.auth.common.SecurityValue.VALID_REFRESH_TOKEN; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; @@ -55,17 +57,16 @@ void Return_new_jwt_if_access_and_refresh_is_exist() { HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); - given(jwtService.extractAccessToken(any(HttpServletRequest.class))).willReturn("access token"); + given(jwtService.extractAccessToken(any(HttpServletRequest.class))).willReturn(INVALID_ACCESS_TOKEN); - given(jwtService.extractRefreshToken(any(HttpServletRequest.class))).willReturn("refresh token"); + given(jwtService.extractRefreshToken(any(HttpServletRequest.class))).willReturn(VALID_REFRESH_TOKEN); given(jwtService.getUserIdFromToken(anyString())).willReturn(1L); - given(jwtService.createAccessToken(anyString())).willReturn("new access token"); - - given(jwtService.createRefreshToken()).willReturn("new refresh token"); + given(jwtService.createServiceToken(any(User.class))).willReturn( + TokenResponse.builder().accessToken("access token").refreshToken("refresh token").build()); - given(redisService.getData(anyString())).willReturn("refresh token"); + given(redisService.getData(anyString())).willReturn(VALID_REFRESH_TOKEN); given(userConnector.findUserById(anyLong())).willReturn(user); @@ -76,8 +77,8 @@ void Return_new_jwt_if_access_and_refresh_is_exist() { // then assertThat(response).isNotNull(); - assertThat(response.getAccessToken()).isEqualTo("new access token"); - assertThat(response.getRefreshToken()).isEqualTo("new refresh token"); + assertThat(response.getAccessToken()).isEqualTo("access token"); + assertThat(response.getRefreshToken()).isEqualTo("refresh token"); verify(userConnector, times(1)).findUserById(anyLong()); verify(jwtService, times(1)).validateToken(any()); }