diff --git a/src/main/java/io/spring/api/ProfileApi.java b/src/main/java/io/spring/api/ProfileApi.java index 4873e54..1ea2c1d 100644 --- a/src/main/java/io/spring/api/ProfileApi.java +++ b/src/main/java/io/spring/api/ProfileApi.java @@ -9,6 +9,7 @@ import io.spring.core.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; +import java.util.Optional; @RestController @RequestMapping(path = "profiles/{username}") @@ -47,6 +49,22 @@ public class ProfileApi { }).orElseThrow(ResourceNotFoundException::new); } + @DeleteMapping(path = "follow") + public ResponseEntity unfollow(@PathVariable("username") String username, + @AuthenticationPrincipal User user) { + Optional userOptional = userRepository.findByUsername(username); + if (userOptional.isPresent()) { + User target = userOptional.get(); + return userRepository.findRelation(user.getId(), target.getId()) + .map(relation -> { + userRepository.removeRelation(relation); + return profileResponse(profileQueryService.findByUsername(username, user).get()); + }).orElseThrow(ResourceNotFoundException::new); + } else { + throw new ResourceNotFoundException(); + } + } + private ResponseEntity profileResponse(ProfileData profile) { return ResponseEntity.ok(new HashMap() {{ put("profile", profile); diff --git a/src/main/java/io/spring/core/user/UserRepository.java b/src/main/java/io/spring/core/user/UserRepository.java index d26465c..42b3570 100644 --- a/src/main/java/io/spring/core/user/UserRepository.java +++ b/src/main/java/io/spring/core/user/UserRepository.java @@ -17,4 +17,6 @@ public interface UserRepository { void saveRelation(FollowRelation followRelation); Optional findRelation(String userId, String targetId); + + void removeRelation(FollowRelation followRelation); } diff --git a/src/main/java/io/spring/infrastructure/user/MyBatisUserRepository.java b/src/main/java/io/spring/infrastructure/user/MyBatisUserRepository.java index 00ce438..ac08683 100644 --- a/src/main/java/io/spring/infrastructure/user/MyBatisUserRepository.java +++ b/src/main/java/io/spring/infrastructure/user/MyBatisUserRepository.java @@ -43,11 +43,18 @@ public class MyBatisUserRepository implements UserRepository { @Override public void saveRelation(FollowRelation followRelation) { - userMapper.saveRelation(followRelation); + if (!findRelation(followRelation.getUserId(), followRelation.getTargetId()).isPresent()) { + userMapper.saveRelation(followRelation); + } } @Override public Optional findRelation(String userId, String targetId) { return Optional.ofNullable(userMapper.findRelation(userId, targetId)); } + + @Override + public void removeRelation(FollowRelation followRelation) { + userMapper.deleteRelation(followRelation); + } } diff --git a/src/main/java/io/spring/infrastructure/user/UserMapper.java b/src/main/java/io/spring/infrastructure/user/UserMapper.java index 7ce8170..007d551 100644 --- a/src/main/java/io/spring/infrastructure/user/UserMapper.java +++ b/src/main/java/io/spring/infrastructure/user/UserMapper.java @@ -21,4 +21,6 @@ public interface UserMapper { FollowRelation findRelation(@Param("userId") String userId, @Param("targetId") String targetId); void saveRelation(@Param("followRelation") FollowRelation followRelation); + + void deleteRelation(@Param("followRelation") FollowRelation followRelation); } diff --git a/src/main/resources/mapper/UserMapper.xml b/src/main/resources/mapper/UserMapper.xml index cf7160b..1635ab9 100644 --- a/src/main/resources/mapper/UserMapper.xml +++ b/src/main/resources/mapper/UserMapper.xml @@ -25,6 +25,9 @@ where id = #{user.id} + + delete from follows where user_id = #{followRelation.userId} and follow_id = #{followRelation.targetId} + diff --git a/src/test/java/io/spring/api/ProfileApiTest.java b/src/test/java/io/spring/api/ProfileApiTest.java index 663cea7..29fa96c 100644 --- a/src/test/java/io/spring/api/ProfileApiTest.java +++ b/src/test/java/io/spring/api/ProfileApiTest.java @@ -3,7 +3,9 @@ package io.spring.api; import io.restassured.RestAssured; import io.spring.application.profile.ProfileData; import io.spring.application.profile.ProfileQueryService; +import io.spring.application.profile.UserRelationshipQueryService; import io.spring.core.article.Article; +import io.spring.core.user.FollowRelation; import io.spring.core.user.User; import io.spring.core.user.UserRepository; import org.junit.Before; @@ -18,7 +20,9 @@ import java.util.Optional; import static io.restassured.RestAssured.given; import static org.hamcrest.core.IsEqual.equalTo; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(SpringRunner.class) @@ -40,6 +44,7 @@ public class ProfileApiTest extends TestWithCurrentUser { userFixture(); anotherUser = new User("username@test.com", "username", "123", "", ""); profileData = new ProfileData(anotherUser.getId(), anotherUser.getUsername(), anotherUser.getBio(), anotherUser.getImage(), false); + when(userRepository.findByUsername(eq(anotherUser.getUsername()))).thenReturn(Optional.of(anotherUser)); } @Test @@ -56,7 +61,6 @@ public class ProfileApiTest extends TestWithCurrentUser { @Test public void should_follow_user_success() throws Exception { - when(userRepository.findByUsername(eq(anotherUser.getUsername()))).thenReturn(Optional.of(anotherUser)); when(profileQueryService.findByUsername(eq(profileData.getUsername()), eq(user))).thenReturn(Optional.of(profileData)); given() .header("Authorization", "Token " + token) @@ -65,6 +69,23 @@ public class ProfileApiTest extends TestWithCurrentUser { .prettyPeek() .then() .statusCode(200); + verify(userRepository).saveRelation(new FollowRelation(user.getId(), anotherUser.getId())); + } + @Test + public void should_unfollow_user_success() throws Exception { + FollowRelation followRelation = new FollowRelation(user.getId(), anotherUser.getId()); + when(userRepository.findRelation(eq(user.getId()), eq(anotherUser.getId()))).thenReturn(Optional.of(followRelation)); + when(profileQueryService.findByUsername(eq(profileData.getUsername()), eq(user))).thenReturn(Optional.of(profileData)); + + given() + .header("Authorization", "Token " + token) + .when() + .delete("/profiles/{username}/follow", anotherUser.getUsername()) + .prettyPeek() + .then() + .statusCode(200); + + verify(userRepository).removeRelation(eq(followRelation)); } } \ No newline at end of file diff --git a/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java b/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java index ef72d4c..4a5dc18 100644 --- a/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java +++ b/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java @@ -65,4 +65,16 @@ public class MyBatisUserRepositoryTest { userRepository.saveRelation(followRelation); assertThat(userRepository.findRelation(user.getId(), other.getId()).isPresent(), is(true)); } + + @Test + public void should_unfollow_user_success() throws Exception { + User other = new User("other@example.com", "other", "123", "", ""); + userRepository.save(other); + + FollowRelation followRelation = new FollowRelation(user.getId(), other.getId()); + userRepository.saveRelation(followRelation); + + userRepository.removeRelation(followRelation); + assertThat(userRepository.findRelation(user.getId(), other.getId()).isPresent(), is(false)); + } } \ No newline at end of file