From ba01b3c245aae99dabd5ae91bdb744c108bf0c4d Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Wed, 23 Feb 2022 12:12:21 +0900 Subject: [PATCH 01/10] =?UTF-8?q?refactor=20:=20ObjectMapper=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/auth/AuthConfig.java | 4 ++-- .../subway/unit/TokenAuthenticationInterceptorTest.java | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/nextstep/auth/AuthConfig.java b/src/main/java/nextstep/auth/AuthConfig.java index db64b8fb1..63327732f 100644 --- a/src/main/java/nextstep/auth/AuthConfig.java +++ b/src/main/java/nextstep/auth/AuthConfig.java @@ -22,10 +22,10 @@ public class AuthConfig implements WebMvcConfigurer { private ObjectMapper objectMapper; private Authorizor authorizor; - public AuthConfig(UserDetailsService userDetailsService, JwtTokenProvider jwtTokenProvider) { + public AuthConfig(UserDetailsService userDetailsService, JwtTokenProvider jwtTokenProvider, ObjectMapper objectMapper) { this.userDetailsService = userDetailsService; this.jwtTokenProvider = jwtTokenProvider; - this.objectMapper = new ObjectMapper(); + this.objectMapper = objectMapper; this.authorizor = new Authorizor(); } diff --git a/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java b/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java index 39f655ade..719f4157e 100644 --- a/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java +++ b/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import nextstep.auth.authentication.AuthenticationToken; +import nextstep.auth.authentication.Authorizor; import nextstep.auth.authentication.TokenAuthenticationInterceptor; import nextstep.auth.context.Authentication; import nextstep.auth.token.JwtTokenProvider; @@ -34,6 +35,7 @@ class TokenAuthenticationInterceptorTest { public static final String JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.ih1aovtQShabQ7l0cINw4k1fagApg3qLWiB8Kt59Lno"; private ObjectMapper objectMapper; + private Authorizor authorizor; @Mock private CustomUserDetailsService userDetailsService; @@ -44,12 +46,13 @@ class TokenAuthenticationInterceptorTest { @BeforeEach void init() { objectMapper = new ObjectMapper(); + authorizor = new Authorizor(); } @Test void convert() throws IOException { // given - TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper); + TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper, authorizor); MockHttpServletRequest request = createMockRequest(); // when @@ -62,7 +65,7 @@ void convert() throws IOException { @Test void authenticate() { - TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper); + TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper, authorizor); when(userDetailsService.loadUserByUsername(EMAIL)).thenReturn(new LoginMember(1L, EMAIL, PASSWORD, 20)); @@ -74,7 +77,7 @@ void authenticate() { @Test void preHandle() throws IOException, JSONException { - TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper); + TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper, authorizor); when(userDetailsService.loadUserByUsername(EMAIL)).thenReturn(new LoginMember(1L, EMAIL, PASSWORD, 20)); when(jwtTokenProvider.createToken(anyString())).thenReturn(JWT_TOKEN); From bc0773b1325cc02d80c8a12aee97fe929d2ed644 Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Wed, 23 Feb 2022 12:24:08 +0900 Subject: [PATCH 02/10] =?UTF-8?q?refactor=20:=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=93=A4=EC=96=B4=EA=B0=80=EB=8A=94=20aut?= =?UTF-8?q?henticate=20=ED=95=A8=EC=88=98=EB=A5=BC=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=ED=95=98=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/auth/AuthConfig.java | 9 +++-- .../auth/authentication/Authorizer.java | 33 +++++++++++++++++++ .../auth/authentication/Authorizor.java | 17 ---------- .../SessionAuthenticationInterceptor.java | 17 +++------- .../TokenAuthenticationInterceptor.java | 20 +++-------- .../TokenAuthenticationInterceptorTest.java | 14 ++++---- 6 files changed, 52 insertions(+), 58 deletions(-) create mode 100644 src/main/java/nextstep/auth/authentication/Authorizer.java delete mode 100644 src/main/java/nextstep/auth/authentication/Authorizor.java diff --git a/src/main/java/nextstep/auth/AuthConfig.java b/src/main/java/nextstep/auth/AuthConfig.java index 63327732f..e6e8f0cd2 100644 --- a/src/main/java/nextstep/auth/AuthConfig.java +++ b/src/main/java/nextstep/auth/AuthConfig.java @@ -1,14 +1,13 @@ package nextstep.auth; import com.fasterxml.jackson.databind.ObjectMapper; -import nextstep.auth.authentication.Authorizor; +import nextstep.auth.authentication.Authorizer; import nextstep.auth.authentication.SessionAuthenticationInterceptor; import nextstep.auth.authentication.TokenAuthenticationInterceptor; import nextstep.auth.authorization.AuthenticationPrincipalArgumentResolver; import nextstep.auth.authorization.SessionSecurityContextPersistenceInterceptor; import nextstep.auth.authorization.TokenSecurityContextPersistenceInterceptor; import nextstep.auth.token.JwtTokenProvider; -import nextstep.member.application.CustomUserDetailsService; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -20,19 +19,19 @@ public class AuthConfig implements WebMvcConfigurer { private UserDetailsService userDetailsService; private JwtTokenProvider jwtTokenProvider; private ObjectMapper objectMapper; - private Authorizor authorizor; + private Authorizer authorizor; public AuthConfig(UserDetailsService userDetailsService, JwtTokenProvider jwtTokenProvider, ObjectMapper objectMapper) { this.userDetailsService = userDetailsService; this.jwtTokenProvider = jwtTokenProvider; this.objectMapper = objectMapper; - this.authorizor = new Authorizor(); + this.authorizor = new Authorizer(userDetailsService); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SessionAuthenticationInterceptor(userDetailsService, authorizor)).addPathPatterns("/login/session"); - registry.addInterceptor(new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper, authorizor)) + registry.addInterceptor(new TokenAuthenticationInterceptor(jwtTokenProvider, objectMapper, authorizor)) .addPathPatterns("/login/token"); registry.addInterceptor(new SessionSecurityContextPersistenceInterceptor()); registry.addInterceptor(new TokenSecurityContextPersistenceInterceptor(jwtTokenProvider)); diff --git a/src/main/java/nextstep/auth/authentication/Authorizer.java b/src/main/java/nextstep/auth/authentication/Authorizer.java new file mode 100644 index 000000000..374259678 --- /dev/null +++ b/src/main/java/nextstep/auth/authentication/Authorizer.java @@ -0,0 +1,33 @@ +package nextstep.auth.authentication; + +import nextstep.auth.User; +import nextstep.auth.UserDetailsService; +import nextstep.auth.context.Authentication; +import org.springframework.util.ObjectUtils; + +public class Authorizer { + + private UserDetailsService userDetailsService; + + public Authorizer(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public void checkAuthentication(User userDetails, AuthenticationToken token) { + if (ObjectUtils.isEmpty(userDetails)) { + throw new AuthenticationException(); + } + + if (!userDetails.checkPassword(token.getCredentials())) { + throw new AuthenticationException(); + } + } + + public Authentication authenticate(AuthenticationToken authenticationToken) { + final String principal = authenticationToken.getPrincipal(); + final User userDetails = userDetailsService.loadUserByUsername(principal); + checkAuthentication(userDetails, authenticationToken); + + return new Authentication(userDetails); + } +} diff --git a/src/main/java/nextstep/auth/authentication/Authorizor.java b/src/main/java/nextstep/auth/authentication/Authorizor.java deleted file mode 100644 index 018c46d9a..000000000 --- a/src/main/java/nextstep/auth/authentication/Authorizor.java +++ /dev/null @@ -1,17 +0,0 @@ -package nextstep.auth.authentication; - -import nextstep.auth.User; -import org.springframework.util.ObjectUtils; - -public class Authorizor { - - public void checkAuthentication(User userDetails, AuthenticationToken token) { - if (ObjectUtils.isEmpty(userDetails)) { - throw new AuthenticationException(); - } - - if (!userDetails.checkPassword(token.getCredentials())) { - throw new AuthenticationException(); - } - } -} diff --git a/src/main/java/nextstep/auth/authentication/SessionAuthenticationInterceptor.java b/src/main/java/nextstep/auth/authentication/SessionAuthenticationInterceptor.java index 610a16f54..10b1a7704 100644 --- a/src/main/java/nextstep/auth/authentication/SessionAuthenticationInterceptor.java +++ b/src/main/java/nextstep/auth/authentication/SessionAuthenticationInterceptor.java @@ -1,6 +1,5 @@ package nextstep.auth.authentication; -import nextstep.auth.User; import nextstep.auth.UserDetailsService; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContext; @@ -20,17 +19,17 @@ public class SessionAuthenticationInterceptor implements HandlerInterceptor, Aut public static final String PASSWORD_FIELD = "password"; private UserDetailsService userDetailsService; - private Authorizor authorizor; + private Authorizer authorizor; - public SessionAuthenticationInterceptor(UserDetailsService userDetailsService, Authorizor authorizor) { + public SessionAuthenticationInterceptor(UserDetailsService userDetailsService, Authorizer authorizor) { this.userDetailsService = userDetailsService; this.authorizor = authorizor; } @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { AuthenticationToken token = convert(request); - Authentication authentication = authenticate(token); + Authentication authentication = authorizor.authenticate(token); HttpSession httpSession = request.getSession(); httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, new SecurityContext(authentication)); @@ -38,14 +37,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return false; } - public Authentication authenticate(AuthenticationToken token) { - String principal = token.getPrincipal(); - User userDetails = userDetailsService.loadUserByUsername(principal); - authorizor.checkAuthentication(userDetails, token); - - return new Authentication(userDetails); - } - @Override public AuthenticationToken convert(HttpServletRequest request) { Map<String, String[]> paramMap = request.getParameterMap(); diff --git a/src/main/java/nextstep/auth/authentication/TokenAuthenticationInterceptor.java b/src/main/java/nextstep/auth/authentication/TokenAuthenticationInterceptor.java index 216cd21ca..e5881c1dd 100644 --- a/src/main/java/nextstep/auth/authentication/TokenAuthenticationInterceptor.java +++ b/src/main/java/nextstep/auth/authentication/TokenAuthenticationInterceptor.java @@ -1,7 +1,6 @@ package nextstep.auth.authentication; import com.fasterxml.jackson.databind.ObjectMapper; -import nextstep.auth.User; import nextstep.auth.UserDetailsService; import nextstep.auth.context.Authentication; import nextstep.auth.token.JwtTokenProvider; @@ -16,16 +15,13 @@ public class TokenAuthenticationInterceptor implements HandlerInterceptor, AuthenticationConverter { - private UserDetailsService userDetailsService; private JwtTokenProvider jwtTokenProvider; private ObjectMapper objectMapper; - private Authorizor authorizor; + private Authorizer authorizor; - public TokenAuthenticationInterceptor(UserDetailsService userDetailsService, - JwtTokenProvider jwtTokenProvider, + public TokenAuthenticationInterceptor(JwtTokenProvider jwtTokenProvider, ObjectMapper objectMapper, - Authorizor authorizor) { - this.userDetailsService = userDetailsService; + Authorizer authorizor) { this.jwtTokenProvider = jwtTokenProvider; this.objectMapper = objectMapper; this.authorizor = authorizor; @@ -34,7 +30,7 @@ public TokenAuthenticationInterceptor(UserDetailsService userDetailsService, @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { AuthenticationToken authenticationToken = convert(request); - Authentication authentication = authenticate(authenticationToken); + Authentication authentication = authorizor.authenticate(authenticationToken); final String payload = objectMapper.writeValueAsString(authentication.getPrincipal()); final String token = jwtTokenProvider.createToken(payload); @@ -49,14 +45,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return false; } - public Authentication authenticate(AuthenticationToken authenticationToken) { - final String principal = authenticationToken.getPrincipal(); - final User userDetails = userDetailsService.loadUserByUsername(principal); - authorizor.checkAuthentication(userDetails, authenticationToken); - - return new Authentication(userDetails); - } - @Override public AuthenticationToken convert(HttpServletRequest request) throws IOException { TokenRequest tokenRequest = objectMapper.readValue(request.getInputStream(), TokenRequest.class); diff --git a/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java b/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java index 719f4157e..8c6766c87 100644 --- a/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java +++ b/src/test/java/nextstep/subway/unit/TokenAuthenticationInterceptorTest.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import nextstep.auth.authentication.AuthenticationToken; -import nextstep.auth.authentication.Authorizor; +import nextstep.auth.authentication.Authorizer; import nextstep.auth.authentication.TokenAuthenticationInterceptor; import nextstep.auth.context.Authentication; import nextstep.auth.token.JwtTokenProvider; @@ -35,7 +35,7 @@ class TokenAuthenticationInterceptorTest { public static final String JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.ih1aovtQShabQ7l0cINw4k1fagApg3qLWiB8Kt59Lno"; private ObjectMapper objectMapper; - private Authorizor authorizor; + private Authorizer authorizor; @Mock private CustomUserDetailsService userDetailsService; @@ -46,13 +46,13 @@ class TokenAuthenticationInterceptorTest { @BeforeEach void init() { objectMapper = new ObjectMapper(); - authorizor = new Authorizor(); + authorizor = new Authorizer(userDetailsService); } @Test void convert() throws IOException { // given - TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper, authorizor); + TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(jwtTokenProvider, objectMapper, authorizor); MockHttpServletRequest request = createMockRequest(); // when @@ -65,19 +65,19 @@ void convert() throws IOException { @Test void authenticate() { - TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper, authorizor); + TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(jwtTokenProvider, objectMapper, authorizor); when(userDetailsService.loadUserByUsername(EMAIL)).thenReturn(new LoginMember(1L, EMAIL, PASSWORD, 20)); AuthenticationToken authenticationToken = new AuthenticationToken(EMAIL, PASSWORD); - Authentication authentication = interceptor.authenticate(authenticationToken); + Authentication authentication = authorizor.authenticate(authenticationToken); assertThat(authentication.getPrincipal()).isNotNull(); } @Test void preHandle() throws IOException, JSONException { - TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(userDetailsService, jwtTokenProvider, objectMapper, authorizor); + TokenAuthenticationInterceptor interceptor = new TokenAuthenticationInterceptor(jwtTokenProvider, objectMapper, authorizor); when(userDetailsService.loadUserByUsername(EMAIL)).thenReturn(new LoginMember(1L, EMAIL, PASSWORD, 20)); when(jwtTokenProvider.createToken(anyString())).thenReturn(JWT_TOKEN); From d6285c737a0ccec33e498049e2591e6bb27f8763 Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Wed, 23 Feb 2022 18:51:12 +0900 Subject: [PATCH 03/10] =?UTF-8?q?test=20:=20test=20=ED=8B=80=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../acceptance/FavoritesAcceptanceTest.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java new file mode 100644 index 000000000..35bc7d6ef --- /dev/null +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -0,0 +1,54 @@ +package nextstep.subway.acceptance; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("즐겨찾기") +public class FavoritesAcceptanceTest extends AcceptanceTest { + + /** + * Given 지하철역 등록되어 있음 + * And 지하철 노선 등록되어 있음 + * And 지하철 노선에 지하철역 등록되어 있음 + * And 회원 등록되어 있음 + * And 로그인 되어있음 + */ + private void init() { + + } + + /** + * When 즐겨찾기 생성을 요청 + * Then 즐겨찾기 생성됨 + */ + @DisplayName("즐겨찾기를 생성하다.") + @Test + void createFavorites() { + init(); + + } + + /** + * given 즐겨찾기 생성 + * When 즐겨찾기 목록 조회 요청 + * Then 즐겨찾기 목록 조회됨 + */ + @DisplayName("즐겨찾기를 조회하다.") + @Test + void showFavorites() { + init(); + + } + + /** + * given 즐겨찾기 생성 + * When 즐겨찾기 삭제 요청 + * Then 즐겨찾기 삭제됨 + */ + @DisplayName("즐가찾기를 삭제하다") + @Test + void removeFavorites() { + init(); + + } +} \ No newline at end of file From 93395aae2ba34adf134c6ecd155d05b4e094e523 Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Wed, 23 Feb 2022 19:44:13 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat=20&=20test=20:=20=EC=A6=90=EA=B2=A8?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/application/MemberService.java | 27 +++++++-- .../application/dto/FavoriteRequest.java | 15 +++++ .../application/dto/FavoriteResponse.java | 37 ++++++++++++ .../java/nextstep/member/domain/Favorite.java | 49 ++++++++++++++++ .../member/domain/FavoriteRepository.java | 7 +++ .../nextstep/member/ui/MemberController.java | 12 ++++ .../acceptance/FavoritesAcceptanceTest.java | 57 +++++++++++++++++-- .../subway/acceptance/FavoritesSteps.java | 30 ++++++++++ 8 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 src/main/java/nextstep/member/application/dto/FavoriteRequest.java create mode 100644 src/main/java/nextstep/member/application/dto/FavoriteResponse.java create mode 100644 src/main/java/nextstep/member/domain/Favorite.java create mode 100644 src/main/java/nextstep/member/domain/FavoriteRepository.java create mode 100644 src/test/java/nextstep/subway/acceptance/FavoritesSteps.java diff --git a/src/main/java/nextstep/member/application/MemberService.java b/src/main/java/nextstep/member/application/MemberService.java index 434adea8a..eecee6cae 100644 --- a/src/main/java/nextstep/member/application/MemberService.java +++ b/src/main/java/nextstep/member/application/MemberService.java @@ -1,18 +1,27 @@ package nextstep.member.application; +import nextstep.member.application.dto.FavoriteRequest; +import nextstep.member.application.dto.FavoriteResponse; import nextstep.member.application.dto.MemberRequest; import nextstep.member.application.dto.MemberResponse; -import nextstep.member.domain.LoginMember; -import nextstep.member.domain.Member; -import nextstep.member.domain.MemberRepository; +import nextstep.member.domain.*; +import nextstep.subway.applicaion.StationService; +import nextstep.subway.domain.Station; import org.springframework.stereotype.Service; @Service public class MemberService { + private MemberRepository memberRepository; + private FavoriteRepository favoriteRepository; + private StationService stationService; - public MemberService(MemberRepository memberRepository) { + public MemberService(MemberRepository memberRepository, + FavoriteRepository favoriteRepository, + StationService stationService) { this.memberRepository = memberRepository; + this.favoriteRepository = favoriteRepository; + this.stationService = stationService; } public MemberResponse createMember(MemberRequest request) { @@ -53,4 +62,14 @@ public void deleteMemberOfMine(LoginMember loginMember) { Member member = memberRepository.findByEmail(loginMember.getEmail()).orElseThrow(RuntimeException::new); memberRepository.delete(member); } + + public FavoriteResponse createFavorite(LoginMember loginMember, FavoriteRequest favoriteRequest) { + Member member = memberRepository.findById(loginMember.getId()).orElseThrow(RuntimeException::new); + Station source = stationService.findById(favoriteRequest.getSource()); + Station target = stationService.findById(favoriteRequest.getTarget()); + Favorite favorite = new Favorite(member, source, target); + favoriteRepository.save(favorite); + + return FavoriteResponse.of(favorite); + } } \ No newline at end of file diff --git a/src/main/java/nextstep/member/application/dto/FavoriteRequest.java b/src/main/java/nextstep/member/application/dto/FavoriteRequest.java new file mode 100644 index 000000000..09e9556e0 --- /dev/null +++ b/src/main/java/nextstep/member/application/dto/FavoriteRequest.java @@ -0,0 +1,15 @@ +package nextstep.member.application.dto; + +public class FavoriteRequest { + + private Long source; + private Long target; + + public Long getSource() { + return source; + } + + public Long getTarget() { + return target; + } +} \ No newline at end of file diff --git a/src/main/java/nextstep/member/application/dto/FavoriteResponse.java b/src/main/java/nextstep/member/application/dto/FavoriteResponse.java new file mode 100644 index 000000000..8065ada60 --- /dev/null +++ b/src/main/java/nextstep/member/application/dto/FavoriteResponse.java @@ -0,0 +1,37 @@ +package nextstep.member.application.dto; + +import nextstep.member.domain.Favorite; +import nextstep.subway.applicaion.dto.StationResponse; + +public class FavoriteResponse { + + private Long id; + private StationResponse source; + private StationResponse target; + + public static FavoriteResponse of(Favorite favorite) { + + return new FavoriteResponse( + favorite.getId(), + StationResponse.of(favorite.getSource()), + StationResponse.of(favorite.getTarget())); + } + + public FavoriteResponse(Long id, StationResponse source, StationResponse target) { + this.id = id; + this.source = source; + this.target = target; + } + + public Long getId() { + return id; + } + + public StationResponse getSource() { + return source; + } + + public StationResponse getTarget() { + return target; + } +} \ No newline at end of file diff --git a/src/main/java/nextstep/member/domain/Favorite.java b/src/main/java/nextstep/member/domain/Favorite.java new file mode 100644 index 000000000..c9e1c8c06 --- /dev/null +++ b/src/main/java/nextstep/member/domain/Favorite.java @@ -0,0 +1,49 @@ +package nextstep.member.domain; + +import nextstep.subway.domain.Station; + +import javax.persistence.*; + +@Entity +public class Favorite { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(cascade = CascadeType.PERSIST) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(cascade = CascadeType.PERSIST) + @JoinColumn(name = "up_station_id") + private Station source; + + @ManyToOne(cascade = CascadeType.PERSIST) + @JoinColumn(name = "down_station_id") + private Station target; + + protected Favorite() {} + + public Favorite(Member member, Station source, Station target) { + this.member = member; + this.source = source; + this.target = target; + } + + public Long getId() { + return id; + } + + public Member getMember() { + return member; + } + + public Station getSource() { + return source; + } + + public Station getTarget() { + return target; + } +} \ No newline at end of file diff --git a/src/main/java/nextstep/member/domain/FavoriteRepository.java b/src/main/java/nextstep/member/domain/FavoriteRepository.java new file mode 100644 index 000000000..862afd78f --- /dev/null +++ b/src/main/java/nextstep/member/domain/FavoriteRepository.java @@ -0,0 +1,7 @@ +package nextstep.member.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FavoriteRepository extends JpaRepository<Favorite, Long> { + +} \ No newline at end of file diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index 96c4f7528..495c39062 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -4,6 +4,8 @@ import nextstep.auth.context.SecurityContext; import nextstep.auth.context.SecurityContextHolder; import nextstep.member.application.MemberService; +import nextstep.member.application.dto.FavoriteRequest; +import nextstep.member.application.dto.FavoriteResponse; import nextstep.member.application.dto.MemberRequest; import nextstep.member.application.dto.MemberResponse; import nextstep.member.domain.LoginMember; @@ -69,5 +71,15 @@ public ResponseEntity<MemberResponse> deleteMemberOfMine(@AuthenticationPrincipa return ResponseEntity.noContent().build(); } + + @PostMapping("/favorites") + public ResponseEntity<Void> createFavorite( + @AuthenticationPrincipal LoginMember loginMember, + @RequestBody FavoriteRequest request + ) { + FavoriteResponse favorite = memberService.createFavorite(loginMember, request); + + return ResponseEntity.created(URI.create("/favorites/" + favorite.getId())).build(); + } } diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index 35bc7d6ef..a889943cb 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -1,11 +1,29 @@ package nextstep.subway.acceptance; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; + +import java.util.HashMap; +import java.util.Map; + +import static nextstep.subway.acceptance.FavoritesSteps.즐겨찾기_생성_요청; +import static nextstep.subway.acceptance.LineSteps.지하철_노선_생성_요청; +import static nextstep.subway.acceptance.MemberSteps.로그인_되어_있음; +import static nextstep.subway.acceptance.MemberSteps.회원_생성_요청; +import static nextstep.subway.acceptance.StationSteps.지하철역_생성_요청; +import static org.assertj.core.api.Assertions.assertThat; @DisplayName("즐겨찾기") public class FavoritesAcceptanceTest extends AcceptanceTest { + private static final String EMAIL = "email@email.com"; + private static final String PASSWORD = "password"; + private static final Integer AGE = 20; + /** * Given 지하철역 등록되어 있음 * And 지하철 노선 등록되어 있음 @@ -13,8 +31,21 @@ public class FavoritesAcceptanceTest extends AcceptanceTest { * And 회원 등록되어 있음 * And 로그인 되어있음 */ - private void init() { + private Long 강남역; + private Long 양재역; + private Long 신분당선; + private String accessToken; + + @BeforeEach + private void init() { + super.setUp(); + 강남역 = 지하철역_생성_요청("강남역").jsonPath().getLong("id"); + 양재역 = 지하철역_생성_요청("양재역").jsonPath().getLong("id"); + Map<String, String> lineCreateParams = createLineCreateParams(강남역, 양재역); + 신분당선 = 지하철_노선_생성_요청(lineCreateParams).jsonPath().getLong("id"); + 회원_생성_요청(EMAIL, PASSWORD, AGE); + accessToken = 로그인_되어_있음(EMAIL, PASSWORD); } /** @@ -24,8 +55,12 @@ private void init() { @DisplayName("즐겨찾기를 생성하다.") @Test void createFavorites() { - init(); + // when + ExtractableResponse<Response> result = 즐겨찾기_생성_요청(accessToken, 강남역, 양재역); + // then + assertThat(result.response().statusCode()).isEqualTo(HttpStatus.CREATED.value()); + assertThat(result.header("Location")).isNotBlank(); } /** @@ -36,7 +71,7 @@ void createFavorites() { @DisplayName("즐겨찾기를 조회하다.") @Test void showFavorites() { - init(); + } @@ -48,7 +83,19 @@ void showFavorites() { @DisplayName("즐가찾기를 삭제하다") @Test void removeFavorites() { - init(); + + } + + private Map<String, String> createLineCreateParams(Long upStationId, Long downStationId) { + Map<String, String> lineCreateParams; + lineCreateParams = new HashMap<>(); + lineCreateParams.put("name", "신분당선"); + lineCreateParams.put("color", "bg-red-600"); + lineCreateParams.put("upStationId", upStationId + ""); + lineCreateParams.put("downStationId", downStationId + ""); + lineCreateParams.put("distance", 10 + ""); + + return lineCreateParams; } -} \ No newline at end of file +} diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java new file mode 100644 index 000000000..c3526a235 --- /dev/null +++ b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java @@ -0,0 +1,30 @@ +package nextstep.subway.acceptance; + +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +import java.util.HashMap; +import java.util.Map; + +public class FavoritesSteps { + + public static ExtractableResponse<Response> 즐겨찾기_생성_요청(final String accessToken, final Long source, final Long target) { + + Map<String, String> favorites; + favorites = new HashMap<>(); + favorites.put("source", source + ""); + favorites.put("target", target + ""); + + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .body(favorites) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().post("/favorites") + .then().log().all() + .statusCode(HttpStatus.CREATED.value()) + .extract(); + } +} From ff5dd7502e326e7674e43b93e8e8dd7d33df5b1e Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Wed, 23 Feb 2022 20:17:15 +0900 Subject: [PATCH 05/10] =?UTF-8?q?test=20&=20feat=20:=20=EC=A6=90=EA=B2=A8?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=EB=A5=BC=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/application/MemberService.java | 11 ++++++++ .../nextstep/member/domain/Favorites.java | 27 +++++++++++++++++++ .../java/nextstep/member/domain/Member.java | 21 ++++++++++++--- .../nextstep/member/ui/MemberController.java | 8 ++++++ .../acceptance/FavoritesAcceptanceTest.java | 8 ++++++ .../subway/acceptance/FavoritesSteps.java | 11 ++++++++ 6 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/main/java/nextstep/member/domain/Favorites.java diff --git a/src/main/java/nextstep/member/application/MemberService.java b/src/main/java/nextstep/member/application/MemberService.java index eecee6cae..e841edee1 100644 --- a/src/main/java/nextstep/member/application/MemberService.java +++ b/src/main/java/nextstep/member/application/MemberService.java @@ -9,6 +9,9 @@ import nextstep.subway.domain.Station; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.stream.Collectors; + @Service public class MemberService { @@ -72,4 +75,12 @@ public FavoriteResponse createFavorite(LoginMember loginMember, FavoriteRequest return FavoriteResponse.of(favorite); } + + public List<FavoriteResponse> findFavoritesOfMine(LoginMember loginMember) { + Member member = memberRepository.findById(loginMember.getId()).orElseThrow(RuntimeException::new); + + return member.getFavorites().stream() + .map(FavoriteResponse::of) + .collect(Collectors.toList()); + } } \ No newline at end of file diff --git a/src/main/java/nextstep/member/domain/Favorites.java b/src/main/java/nextstep/member/domain/Favorites.java new file mode 100644 index 000000000..c11dc3a21 --- /dev/null +++ b/src/main/java/nextstep/member/domain/Favorites.java @@ -0,0 +1,27 @@ +package nextstep.member.domain; + +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.OneToMany; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Embeddable +public class Favorites { + + @OneToMany(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) + private List<Favorite> favorites = new ArrayList<>(); + + public void add(Favorite favorite) { + this.favorites.add(favorite); + } + + public void remove(Favorite favorite) { + this.favorites.remove(favorite); + } + + public List<Favorite> getFavorites() { + return Collections.unmodifiableList(favorites); + } +} \ No newline at end of file diff --git a/src/main/java/nextstep/member/domain/Member.java b/src/main/java/nextstep/member/domain/Member.java index 09ce656ed..0deb5cfaf 100644 --- a/src/main/java/nextstep/member/domain/Member.java +++ b/src/main/java/nextstep/member/domain/Member.java @@ -2,10 +2,8 @@ import nextstep.subway.domain.BaseEntity; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; +import javax.persistence.*; +import java.util.List; @Entity public class Member extends BaseEntity { @@ -16,6 +14,9 @@ public class Member extends BaseEntity { private String password; private Integer age; + @Embedded + private Favorites favorites = new Favorites(); + protected Member() { } @@ -47,4 +48,16 @@ public void update(Member member) { this.password = member.password; this.age = member.age; } + + public List<Favorite> getFavorites() { + return favorites.getFavorites(); + } + + public void addFavorite(Favorite favorite) { + this.favorites.add(favorite); + } + + public void removeFavorite(Favorite favorite) { + this.favorites.remove(favorite); + } } diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index 495c39062..8af4c7838 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.*; import java.net.URI; +import java.util.List; @RestController public class MemberController { @@ -81,5 +82,12 @@ public ResponseEntity<Void> createFavorite( return ResponseEntity.created(URI.create("/favorites/" + favorite.getId())).build(); } + + @GetMapping("/favorites") + public ResponseEntity<List<FavoriteResponse>> findFavoritesOfMine(@AuthenticationPrincipal LoginMember loginMember) { + List<FavoriteResponse> favoriteResponses = memberService.findFavoritesOfMine(loginMember); + + return ResponseEntity.ok().body(favoriteResponses); + } } diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index a889943cb..4bb42b40f 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -10,6 +10,7 @@ import java.util.HashMap; import java.util.Map; +import static nextstep.subway.acceptance.FavoritesSteps.즐겨찾기_목록_조회; import static nextstep.subway.acceptance.FavoritesSteps.즐겨찾기_생성_요청; import static nextstep.subway.acceptance.LineSteps.지하철_노선_생성_요청; import static nextstep.subway.acceptance.MemberSteps.로그인_되어_있음; @@ -71,8 +72,15 @@ void createFavorites() { @DisplayName("즐겨찾기를 조회하다.") @Test void showFavorites() { + // given + 즐겨찾기_생성_요청(accessToken, 강남역, 양재역); + // when + ExtractableResponse<Response> result = 즐겨찾기_목록_조회(accessToken); + // then + assertThat(result.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(result.jsonPath().getList("id")).hasSize(1).containsExactly(1); } /** diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java index c3526a235..e44ee8816 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java @@ -27,4 +27,15 @@ public class FavoritesSteps { .statusCode(HttpStatus.CREATED.value()) .extract(); } + + public static ExtractableResponse<Response> 즐겨찾기_목록_조회(final String accessToken) { + + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/favorites") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + } } From d838e0ae598241534bbbbda448a2e0fa53bbd15f Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Wed, 23 Feb 2022 20:27:05 +0900 Subject: [PATCH 06/10] =?UTF-8?q?test=20&=20feat=20:=20=EC=A6=90=EA=B2=A8?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=EB=A5=BC=20=EC=82=AD=EC=A0=9C=ED=95=98?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/member/application/MemberService.java | 7 +++++++ src/main/java/nextstep/member/ui/MemberController.java | 10 ++++++++++ .../subway/acceptance/FavoritesAcceptanceTest.java | 9 +++++++-- .../nextstep/subway/acceptance/FavoritesSteps.java | 9 +++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/main/java/nextstep/member/application/MemberService.java b/src/main/java/nextstep/member/application/MemberService.java index e841edee1..9636cdade 100644 --- a/src/main/java/nextstep/member/application/MemberService.java +++ b/src/main/java/nextstep/member/application/MemberService.java @@ -83,4 +83,11 @@ public List<FavoriteResponse> findFavoritesOfMine(LoginMember loginMember) { .map(FavoriteResponse::of) .collect(Collectors.toList()); } + + public void deleteFavorite(LoginMember loginMember, Long favoriteId) { + Member member = memberRepository.findById(loginMember.getId()).orElseThrow(RuntimeException::new); + Favorite favorite = favoriteRepository.findById(favoriteId).orElseThrow(RuntimeException::new); + + member.removeFavorite(favorite); + } } \ No newline at end of file diff --git a/src/main/java/nextstep/member/ui/MemberController.java b/src/main/java/nextstep/member/ui/MemberController.java index 8af4c7838..89b27fe4b 100644 --- a/src/main/java/nextstep/member/ui/MemberController.java +++ b/src/main/java/nextstep/member/ui/MemberController.java @@ -89,5 +89,15 @@ public ResponseEntity<List<FavoriteResponse>> findFavoritesOfMine(@Authenticatio return ResponseEntity.ok().body(favoriteResponses); } + + @DeleteMapping("/favorites/{favoriteId}") + public ResponseEntity<MemberResponse> deleteFavoriteOfMine( + @AuthenticationPrincipal LoginMember loginMember, + @PathVariable Long favoriteId + ) { + memberService.deleteFavorite(loginMember, favoriteId); + + return ResponseEntity.noContent().build(); + } } diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index 4bb42b40f..5edb89b6b 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -10,8 +10,7 @@ import java.util.HashMap; import java.util.Map; -import static nextstep.subway.acceptance.FavoritesSteps.즐겨찾기_목록_조회; -import static nextstep.subway.acceptance.FavoritesSteps.즐겨찾기_생성_요청; +import static nextstep.subway.acceptance.FavoritesSteps.*; import static nextstep.subway.acceptance.LineSteps.지하철_노선_생성_요청; import static nextstep.subway.acceptance.MemberSteps.로그인_되어_있음; import static nextstep.subway.acceptance.MemberSteps.회원_생성_요청; @@ -91,8 +90,14 @@ void showFavorites() { @DisplayName("즐가찾기를 삭제하다") @Test void removeFavorites() { + // given + 즐겨찾기_생성_요청(accessToken, 강남역, 양재역); + // when + ExtractableResponse<Response> result = 즐겨찾기_삭제(accessToken, 1L); + // then + assertThat(result.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); } private Map<String, String> createLineCreateParams(Long upStationId, Long downStationId) { diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java index e44ee8816..28e47594a 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java @@ -38,4 +38,13 @@ public class FavoritesSteps { .statusCode(HttpStatus.OK.value()) .extract(); } + + public static ExtractableResponse<Response> 즐겨찾기_삭제(final String accessToken, final Long id) { + + return RestAssured + .given().log().all() + .auth().oauth2(accessToken) + .when().delete("/favorites/{favoriteId}", id) + .then().log().all().extract(); + } } From a21678bd44155ccb19a8e6bcbfb9832c7b356f34 Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Sat, 26 Feb 2022 16:59:33 +0900 Subject: [PATCH 07/10] =?UTF-8?q?refactor=20:=20Authorizer=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EB=A9=94=EC=86=8C=EB=93=9C=20checkAuthentication?= =?UTF-8?q?=20:=20public=20->=20private?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/authentication/Authorizer.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/nextstep/auth/authentication/Authorizer.java b/src/main/java/nextstep/auth/authentication/Authorizer.java index 374259678..ea0ac67d0 100644 --- a/src/main/java/nextstep/auth/authentication/Authorizer.java +++ b/src/main/java/nextstep/auth/authentication/Authorizer.java @@ -13,7 +13,15 @@ public Authorizer(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } - public void checkAuthentication(User userDetails, AuthenticationToken token) { + public Authentication authenticate(AuthenticationToken authenticationToken) { + final String principal = authenticationToken.getPrincipal(); + final User userDetails = userDetailsService.loadUserByUsername(principal); + checkAuthentication(userDetails, authenticationToken); + + return new Authentication(userDetails); + } + + private void checkAuthentication(User userDetails, AuthenticationToken token) { if (ObjectUtils.isEmpty(userDetails)) { throw new AuthenticationException(); } @@ -22,12 +30,4 @@ public void checkAuthentication(User userDetails, AuthenticationToken token) { throw new AuthenticationException(); } } - - public Authentication authenticate(AuthenticationToken authenticationToken) { - final String principal = authenticationToken.getPrincipal(); - final User userDetails = userDetailsService.loadUserByUsername(principal); - checkAuthentication(userDetails, authenticationToken); - - return new Authentication(userDetails); - } } From 0c52d1aef260ce80448973f672ff86aac7766891 Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Sat, 26 Feb 2022 20:45:39 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor=20&=20test=20:=20=EC=A6=90?= =?UTF-8?q?=EA=B2=A8=EC=B0=BE=EA=B8=B0=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B6=80=EB=B6=84=EC=9D=84=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=95=98=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/subway/acceptance/FavoritesAcceptanceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index 5edb89b6b..3dcaef6c7 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -79,7 +79,7 @@ void showFavorites() { // then assertThat(result.statusCode()).isEqualTo(HttpStatus.OK.value()); - assertThat(result.jsonPath().getList("id")).hasSize(1).containsExactly(1); + assertThat(result.jsonPath().getList("id", Long.class)).hasSize(1).containsExactly(1L); } /** From b20453e713b8e0e0cfad1422b45b39468fad8305 Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Sat, 26 Feb 2022 21:14:28 +0900 Subject: [PATCH 09/10] =?UTF-8?q?feat=20&=20test=20:=20=EB=B9=84=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EC=A6=90=EA=B2=A8?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=A0=20=EC=88=98=20=EC=97=86=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...AuthenticationPrincipalArgumentResolver.java | 9 +++++++++ .../acceptance/FavoritesAcceptanceTest.java | 17 +++++++++++++++++ .../subway/acceptance/FavoritesSteps.java | 1 - 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java b/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java index 7c776cb80..47dc98171 100644 --- a/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java +++ b/src/main/java/nextstep/auth/authorization/AuthenticationPrincipalArgumentResolver.java @@ -1,8 +1,10 @@ package nextstep.auth.authorization; +import nextstep.auth.authentication.AuthenticationException; import nextstep.auth.context.Authentication; import nextstep.auth.context.SecurityContextHolder; import org.springframework.core.MethodParameter; +import org.springframework.util.ObjectUtils; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -20,6 +22,7 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + authenticationValidate(authentication); if (authentication.getPrincipal() instanceof Map) { return extractPrincipal(parameter, authentication); } @@ -27,6 +30,12 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m return authentication.getPrincipal(); } + private void authenticationValidate(Authentication authentication) { + if (ObjectUtils.isEmpty(authentication)) { + throw new AuthenticationException(); + } + } + private Object extractPrincipal(MethodParameter parameter, Authentication authentication) { try { Map<String, String> principal = (Map) authentication.getPrincipal(); diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index 3dcaef6c7..270b39b1c 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -100,6 +100,23 @@ void removeFavorites() { assertThat(result.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); } + /** + * 비로그인 경우 401 Unauthorized 응답 + */ + @DisplayName("비로그인 즐겨찾기 기능을 사용할 수 없다.") + @Test + void isUnauthorized() { + // given + 즐겨찾기_생성_요청(accessToken, 강남역, 양재역); + accessToken = ""; + + // when + ExtractableResponse<Response> result = 즐겨찾기_목록_조회(accessToken); + + // then + assertThat(result.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); + } + private Map<String, String> createLineCreateParams(Long upStationId, Long downStationId) { Map<String, String> lineCreateParams; lineCreateParams = new HashMap<>(); diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java index 28e47594a..bc4de5d3c 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesSteps.java @@ -35,7 +35,6 @@ public class FavoritesSteps { .accept(MediaType.APPLICATION_JSON_VALUE) .when().get("/favorites") .then().log().all() - .statusCode(HttpStatus.OK.value()) .extract(); } From cc6bcd8e149d45fbf28fda1345b4dcf561118673 Mon Sep 17 00:00:00 2001 From: minyul <ggomjae@gmail.com> Date: Sat, 26 Feb 2022 22:25:58 +0900 Subject: [PATCH 10/10] =?UTF-8?q?refactor=20:=20=EA=B0=84=EC=A0=91?= =?UTF-8?q?=EC=B0=B8=EC=A1=B0=EB=A1=9C=20=EB=B0=94=EA=BE=B8=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/application/MemberService.java | 8 ++++---- .../java/nextstep/member/domain/Favorite.java | 9 ++++----- .../member/domain/FavoriteRepository.java | 4 ++++ src/main/java/nextstep/member/domain/Member.java | 15 --------------- .../acceptance/FavoritesAcceptanceTest.java | 6 +++--- 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/main/java/nextstep/member/application/MemberService.java b/src/main/java/nextstep/member/application/MemberService.java index 9636cdade..154f9225a 100644 --- a/src/main/java/nextstep/member/application/MemberService.java +++ b/src/main/java/nextstep/member/application/MemberService.java @@ -70,7 +70,7 @@ public FavoriteResponse createFavorite(LoginMember loginMember, FavoriteRequest Member member = memberRepository.findById(loginMember.getId()).orElseThrow(RuntimeException::new); Station source = stationService.findById(favoriteRequest.getSource()); Station target = stationService.findById(favoriteRequest.getTarget()); - Favorite favorite = new Favorite(member, source, target); + Favorite favorite = new Favorite(member.getId(), source, target); favoriteRepository.save(favorite); return FavoriteResponse.of(favorite); @@ -78,16 +78,16 @@ public FavoriteResponse createFavorite(LoginMember loginMember, FavoriteRequest public List<FavoriteResponse> findFavoritesOfMine(LoginMember loginMember) { Member member = memberRepository.findById(loginMember.getId()).orElseThrow(RuntimeException::new); + List<Favorite> favorites = favoriteRepository.findByMember(member.getId()); - return member.getFavorites().stream() + return favorites.stream() .map(FavoriteResponse::of) .collect(Collectors.toList()); } public void deleteFavorite(LoginMember loginMember, Long favoriteId) { - Member member = memberRepository.findById(loginMember.getId()).orElseThrow(RuntimeException::new); Favorite favorite = favoriteRepository.findById(favoriteId).orElseThrow(RuntimeException::new); - member.removeFavorite(favorite); + favoriteRepository.delete(favorite); } } \ No newline at end of file diff --git a/src/main/java/nextstep/member/domain/Favorite.java b/src/main/java/nextstep/member/domain/Favorite.java index c9e1c8c06..2ecca2f50 100644 --- a/src/main/java/nextstep/member/domain/Favorite.java +++ b/src/main/java/nextstep/member/domain/Favorite.java @@ -11,9 +11,8 @@ public class Favorite { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne(cascade = CascadeType.PERSIST) @JoinColumn(name = "member_id") - private Member member; + private Long member; @ManyToOne(cascade = CascadeType.PERSIST) @JoinColumn(name = "up_station_id") @@ -25,8 +24,8 @@ public class Favorite { protected Favorite() {} - public Favorite(Member member, Station source, Station target) { - this.member = member; + public Favorite(Long memberId, Station source, Station target) { + this.member = memberId; this.source = source; this.target = target; } @@ -35,7 +34,7 @@ public Long getId() { return id; } - public Member getMember() { + public Long getMember() { return member; } diff --git a/src/main/java/nextstep/member/domain/FavoriteRepository.java b/src/main/java/nextstep/member/domain/FavoriteRepository.java index 862afd78f..030c61525 100644 --- a/src/main/java/nextstep/member/domain/FavoriteRepository.java +++ b/src/main/java/nextstep/member/domain/FavoriteRepository.java @@ -2,6 +2,10 @@ import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface FavoriteRepository extends JpaRepository<Favorite, Long> { + + List<Favorite> findByMember(Long member); } \ No newline at end of file diff --git a/src/main/java/nextstep/member/domain/Member.java b/src/main/java/nextstep/member/domain/Member.java index 0deb5cfaf..041a9d91b 100644 --- a/src/main/java/nextstep/member/domain/Member.java +++ b/src/main/java/nextstep/member/domain/Member.java @@ -14,9 +14,6 @@ public class Member extends BaseEntity { private String password; private Integer age; - @Embedded - private Favorites favorites = new Favorites(); - protected Member() { } @@ -48,16 +45,4 @@ public void update(Member member) { this.password = member.password; this.age = member.age; } - - public List<Favorite> getFavorites() { - return favorites.getFavorites(); - } - - public void addFavorite(Favorite favorite) { - this.favorites.add(favorite); - } - - public void removeFavorite(Favorite favorite) { - this.favorites.remove(favorite); - } } diff --git a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java index 270b39b1c..efbe17e37 100644 --- a/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/FavoritesAcceptanceTest.java @@ -2,6 +2,7 @@ import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import nextstep.auth.authentication.AuthenticationToken; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,8 +13,7 @@ import static nextstep.subway.acceptance.FavoritesSteps.*; import static nextstep.subway.acceptance.LineSteps.지하철_노선_생성_요청; -import static nextstep.subway.acceptance.MemberSteps.로그인_되어_있음; -import static nextstep.subway.acceptance.MemberSteps.회원_생성_요청; +import static nextstep.subway.acceptance.MemberSteps.*; import static nextstep.subway.acceptance.StationSteps.지하철역_생성_요청; import static org.assertj.core.api.Assertions.assertThat; @@ -105,7 +105,7 @@ void removeFavorites() { */ @DisplayName("비로그인 즐겨찾기 기능을 사용할 수 없다.") @Test - void isUnauthorized() { + void isUnauthorizedByLogin() { // given 즐겨찾기_생성_요청(accessToken, 강남역, 양재역); accessToken = "";