Skip to content

Commit 8196cd4

Browse files
committed
feat: 유저 태그 삭제 API
1 parent 615d623 commit 8196cd4

File tree

8 files changed

+148
-2
lines changed

8 files changed

+148
-2
lines changed

capturecat-core/src/main/java/com/capturecat/core/api/user/UserTagController.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.capturecat.core.api.user;
22

33
import org.springframework.security.core.annotation.AuthenticationPrincipal;
4+
import org.springframework.web.bind.annotation.DeleteMapping;
45
import org.springframework.web.bind.annotation.PostMapping;
56
import org.springframework.web.bind.annotation.RequestMapping;
67
import org.springframework.web.bind.annotation.RequestParam;
@@ -26,4 +27,25 @@ public ApiResponse<TagResponse> create(@AuthenticationPrincipal LoginUser loginU
2627

2728
return ApiResponse.success(tagResponse);
2829
}
30+
31+
32+
33+
34+
35+
36+
37+
38+
39+
40+
41+
42+
43+
44+
45+
@DeleteMapping
46+
public ApiResponse<?> delete(@AuthenticationPrincipal LoginUser loginUser, @RequestParam Long tagId) {
47+
userTagService.delete(loginUser, tagId);
48+
49+
return ApiResponse.success();
50+
}
2951
}

capturecat-core/src/main/java/com/capturecat/core/domain/user/UserTagRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package com.capturecat.core.domain.user;
22

3+
import java.util.Optional;
4+
35
import org.springframework.data.jpa.repository.JpaRepository;
46

57
import com.capturecat.core.domain.tag.Tag;
68

79
public interface UserTagRepository extends JpaRepository<UserTag, Long> {
810

11+
Optional<UserTag> findByUserAndTag(User user, Tag tag);
12+
913
boolean existsByUserAndTag(User user, Tag tag);
1014

1115
long countByUser(User user);

capturecat-core/src/main/java/com/capturecat/core/service/user/UserTagService.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import com.capturecat.core.domain.tag.Tag;
1111
import com.capturecat.core.domain.tag.TagRegister;
12+
import com.capturecat.core.domain.tag.TagRepository;
1213
import com.capturecat.core.domain.user.User;
1314
import com.capturecat.core.domain.user.UserRepository;
1415
import com.capturecat.core.domain.user.UserTag;
@@ -28,6 +29,7 @@ public class UserTagService {
2829
private final UserRepository userRepository;
2930
private final UserTagRepository userTagRepository;
3031
private final TagRegister tagRegister;
32+
private final TagRepository tagRepository;
3133

3234
@Transactional
3335
public TagResponse create(LoginUser loginUser, String tagName) {
@@ -46,6 +48,18 @@ public TagResponse create(LoginUser loginUser, String tagName) {
4648
}
4749
}
4850

51+
@Transactional
52+
public void delete(LoginUser loginUser, Long tagId) {
53+
User user = userRepository.findByUsername(loginUser.getUsername())
54+
.orElseThrow(() -> new CoreException(ErrorType.USER_NOT_FOUND));
55+
Tag tag = tagRepository.findById(tagId)
56+
.orElseThrow(() -> new CoreException(ErrorType.TAG_NOT_FOUND));
57+
UserTag userTag = userTagRepository.findByUserAndTag(user, tag)
58+
.orElseThrow(() -> new CoreException(ErrorType.USER_TAG_NOT_FOUND));
59+
60+
userTagRepository.delete(userTag);
61+
}
62+
4963
private void validate(User user, Tag tag) {
5064
validateDuplicateUserTag(user, tag);
5165
validateUserTagCountLimit(user);

capturecat-core/src/main/java/com/capturecat/core/support/error/ErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public enum ErrorCode {
4040
INTERNAL_SERVER_ERROR("서버에서 오류가 발생했습니다."),
4141
INVALID_LOGOUT_AUTH_TOKEN("ACCESS 토큰 또는 REFRESH 토큰이 유효하지 않습니다."),
4242
ALREADY_EXISTS_USER_TAG("이미 등록된 유저 태그입니다."),
43-
EXCEED_MAX_USER_TAG_COUNT("태그는 한 계정당 최대 30개까지 추가할 수 있어요.");
43+
EXCEED_MAX_USER_TAG_COUNT("태그는 한 계정당 최대 30개까지 추가할 수 있어요."),
44+
NOT_FOUND_USER_TAG("유저 태그를 찾을 수 없습니다.");
4445

4546
private final String message;
4647

capturecat-core/src/main/java/com/capturecat/core/support/error/ErrorType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public enum ErrorType {
4545
MISSING_PARAMETER(HttpStatus.BAD_REQUEST, ErrorCode.MISSING_PARAMETER, LogLevel.WARN),
4646
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, ErrorCode.INTERNAL_SERVER_ERROR, LogLevel.ERROR),
4747
USER_TAG_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, ErrorCode.ALREADY_EXISTS_USER_TAG, LogLevel.WARN),
48-
TOO_MANY_USER_TAGS(HttpStatus.BAD_REQUEST, ErrorCode.EXCEED_MAX_USER_TAG_COUNT, LogLevel.WARN);
48+
TOO_MANY_USER_TAGS(HttpStatus.BAD_REQUEST, ErrorCode.EXCEED_MAX_USER_TAG_COUNT, LogLevel.WARN),
49+
USER_TAG_NOT_FOUND(HttpStatus.NOT_FOUND, ErrorCode.NOT_FOUND_USER_TAG, LogLevel.WARN);
4950

5051
private final HttpStatus status;
5152

capturecat-core/src/test/java/com/capturecat/core/api/user/UserErrorCodeControllerTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.capturecat.core.api.user;
22

3+
import static com.capturecat.core.support.error.ErrorType.TAG_NOT_FOUND;
34
import static com.capturecat.core.support.error.ErrorType.TOO_MANY_USER_TAGS;
45
import static com.capturecat.core.support.error.ErrorType.USER_NOT_FOUND;
56
import static com.capturecat.core.support.error.ErrorType.USER_TAG_ALREADY_EXISTS;
7+
import static com.capturecat.core.support.error.ErrorType.USER_TAG_NOT_FOUND;
68

79
import java.util.List;
810

@@ -19,4 +21,18 @@ class UserErrorCodeControllerTest extends ErrorCodeDocumentTest {
1921
TOO_MANY_USER_TAGS, USER_NOT_FOUND);
2022
generateErrorDocs("errorCode/createUserTag", errorCodeDescriptors);
2123
}
24+
25+
26+
27+
28+
29+
30+
31+
32+
@Test
33+
void 유저_태그_삭제_에러_코드_문서화() {
34+
List<ErrorCodeDescriptor> errorCodeDescriptors = generateErrorCodeDescriptors(USER_NOT_FOUND, TAG_NOT_FOUND,
35+
USER_TAG_NOT_FOUND);
36+
generateErrorDocs("errorCode/deleteUserTag", errorCodeDescriptors);
37+
}
2238
}

capturecat-core/src/test/java/com/capturecat/core/api/user/UserTagControllerTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,23 @@ void setUp() {
6262
fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("태그 ID"),
6363
fieldWithPath("data.name").type(JsonFieldType.STRING).description("태그 이름"))));
6464
}
65+
66+
@Test
67+
void 유저_태그_삭제() {
68+
// given
69+
BDDMockito.given(userTagService.create(any(), anyString())).willReturn(new TagResponse(1L, "java"));
70+
71+
// when & then
72+
given()
73+
.header(HttpHeaders.AUTHORIZATION, JwtUtil.BEARER_PREFIX + ACCESS_TOKEN)
74+
.contentType(ContentType.JSON)
75+
.queryParam("tagId", 1L)
76+
.when().delete("/v1/user-tags")
77+
.then().status(HttpStatus.OK)
78+
.apply(document("deleteUserTag", requestPreprocessor(), responsePreprocessor(),
79+
requestHeaders(headerWithName(HttpHeaders.AUTHORIZATION).description("유효한 Access 토큰")),
80+
queryParameters(parameterWithName("tagId").description("태그 ID")),
81+
responseFields(
82+
fieldWithPath("result").type(JsonFieldType.STRING).description("요청 결과"))));
83+
}
6584
}

capturecat-core/src/test/java/com/capturecat/core/service/user/UserTagServiceTest.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatThrownBy;
55
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.ArgumentMatchers.anyLong;
67
import static org.mockito.ArgumentMatchers.anyString;
78
import static org.mockito.ArgumentMatchers.eq;
89
import static org.mockito.BDDMockito.given;
@@ -21,6 +22,7 @@
2122
import com.capturecat.core.DummyObject;
2223
import com.capturecat.core.domain.tag.TagFixture;
2324
import com.capturecat.core.domain.tag.TagRegister;
25+
import com.capturecat.core.domain.tag.TagRepository;
2426
import com.capturecat.core.domain.user.UserRepository;
2527
import com.capturecat.core.domain.user.UserTagFixture;
2628
import com.capturecat.core.domain.user.UserTagRepository;
@@ -37,6 +39,9 @@ class UserTagServiceTest {
3739
@Mock
3840
UserTagRepository userTagRepository;
3941

42+
@Mock
43+
TagRepository tagRepository;
44+
4045
@Mock
4146
TagRegister tagRegister;
4247

@@ -114,4 +119,68 @@ class UserTagServiceTest {
114119

115120
verify(userTagRepository, never()).save(any());
116121
}
122+
123+
@Test
124+
void 유저_태그를_삭제한다() {
125+
// given
126+
var user = DummyObject.newUser("test");
127+
var tag = TagFixture.createTag(1L, "java");
128+
var userTag = UserTagFixture.createUserTag(1L, user, tag);
129+
130+
given(userRepository.findByUsername(anyString())).willReturn(Optional.of(user));
131+
given(tagRepository.findById(anyLong())).willReturn(Optional.of(tag));
132+
given(userTagRepository.findByUserAndTag(eq(user), eq(tag))).willReturn(Optional.of(userTag));
133+
134+
// when
135+
userTagService.delete(new LoginUser(user), 1L);
136+
137+
// then
138+
verify(userTagRepository, times(1)).delete(eq(userTag));
139+
}
140+
141+
@Test
142+
void 유저_태그_삭제_시_회원이_없으면_실패한다() {
143+
// given
144+
given(userRepository.findByUsername(anyString())).willReturn(Optional.empty());
145+
146+
// when & then
147+
assertThatThrownBy(() -> userTagService.delete(new LoginUser(DummyObject.newUser("test")), 1L))
148+
.isInstanceOf(CoreException.class)
149+
.hasMessage(ErrorType.USER_NOT_FOUND.getCode().getMessage());
150+
151+
verify(userTagRepository, never()).delete(any());
152+
}
153+
154+
@Test
155+
void 유저_태그_삭제_시_태그가_없으면_실패한다() {
156+
// given
157+
var user = DummyObject.newUser("test");
158+
159+
given(userRepository.findByUsername(anyString())).willReturn(Optional.of(user));
160+
given(tagRepository.findById(anyLong())).willReturn(Optional.empty());
161+
162+
// when & then
163+
assertThatThrownBy(() -> userTagService.delete(new LoginUser(DummyObject.newUser("test")), 1L))
164+
.isInstanceOf(CoreException.class)
165+
.hasMessage(ErrorType.TAG_NOT_FOUND.getCode().getMessage());
166+
167+
verify(userTagRepository, never()).delete(any());
168+
}
169+
170+
@Test
171+
void 유저_태그_삭제_시_유저_태그가_없으면_실패한다() {
172+
// given
173+
var user = DummyObject.newUser("test");
174+
var tag = TagFixture.createTag(1L, "java");
175+
176+
given(userRepository.findByUsername(anyString())).willReturn(Optional.of(user));
177+
given(tagRepository.findById(anyLong())).willReturn(Optional.of(tag));
178+
given(userTagRepository.findByUserAndTag(eq(user), eq(tag))).willReturn(Optional.empty());
179+
180+
// when & then
181+
assertThatThrownBy(() -> userTagService.delete(new LoginUser(DummyObject.newUser("test")), 1L))
182+
.isInstanceOf(CoreException.class);
183+
184+
verify(userTagRepository, never()).delete(any());
185+
}
117186
}

0 commit comments

Comments
 (0)