Skip to content

Commit b8c0ab7

Browse files
authored
feat: 유저 태그 수정 API (#124)
* feat: 유저 태그 수정 API * docs: 유저 태그 수정 API * docs: 앵커 추가
1 parent 615d623 commit b8c0ab7

File tree

12 files changed

+229
-2
lines changed

12 files changed

+229
-2
lines changed

capturecat-core/src/docs/asciidoc/error-codes.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,7 @@ include::{snippets}/errorCode/autocomplete/error-codes.adoc[]
9393
[[유저-태그-생성]]
9494
=== 유저 태그 생성
9595
include::{snippets}/errorCode/createUserTag/error-codes.adoc[]
96+
97+
[[유저-태그-수정]]
98+
=== 유저 태그 수정
99+
include::{snippets}/errorCode/updateUserTag/error-codes.adoc[]

capturecat-core/src/docs/asciidoc/user.adoc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,13 @@ operation::createUserTag[snippets='curl-request,http-request,request-headers,que
4848
유저 태그 생성이 실패했다면 HTTP 상태 코드와 함께 <<에러-객체-형식, 에러 객체>>가 돌아옵니다.
4949

5050
<<error-codes#유저-태그-생성, 유저 태그 생성 API에서 발생할 수 있는 에러>>를 살펴보세요.
51+
52+
[[유저-태그-수정]]
53+
=== 유저 태그 수정
54+
==== 성공
55+
operation::updateUserTag[snippets='curl-request,http-request,request-headers,request-fields,http-response,response-fields']
56+
57+
==== 실패
58+
유저 태그 수정이 실패했다면 HTTP 상태 코드와 함께 <<에러-객체-형식, 에러 객체>>가 돌아옵니다.
59+
60+
<<error-codes#유저-태그-수정, 유저 태그 수정 API에서 발생할 수 있는 에러>>를 살펴보세요.

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

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

33
import org.springframework.security.core.annotation.AuthenticationPrincipal;
4+
import org.springframework.web.bind.annotation.PatchMapping;
45
import org.springframework.web.bind.annotation.PostMapping;
6+
import org.springframework.web.bind.annotation.RequestBody;
57
import org.springframework.web.bind.annotation.RequestMapping;
68
import org.springframework.web.bind.annotation.RequestParam;
79
import org.springframework.web.bind.annotation.RestController;
810

911
import lombok.RequiredArgsConstructor;
1012

13+
import com.capturecat.core.api.user.dto.TagRenameRequest;
1114
import com.capturecat.core.service.auth.LoginUser;
1215
import com.capturecat.core.service.image.TagResponse;
1316
import com.capturecat.core.service.user.UserTagService;
@@ -26,4 +29,12 @@ public ApiResponse<TagResponse> create(@AuthenticationPrincipal LoginUser loginU
2629

2730
return ApiResponse.success(tagResponse);
2831
}
32+
33+
@PatchMapping
34+
public ApiResponse<TagResponse> update(@AuthenticationPrincipal LoginUser loginUser,
35+
@RequestBody TagRenameRequest request) {
36+
TagResponse response = userTagService.update(loginUser, request.currentTagId(), request.newTagName());
37+
38+
return ApiResponse.success(response);
39+
}
2940
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.capturecat.core.api.user.dto;
2+
3+
public record TagRenameRequest(Long currentTagId, String newTagName) {
4+
}

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: 20 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;
@@ -27,6 +28,7 @@ public class UserTagService {
2728

2829
private final UserRepository userRepository;
2930
private final UserTagRepository userTagRepository;
31+
private final TagRepository tagRepository;
3032
private final TagRegister tagRegister;
3133

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

51+
@Transactional
52+
public TagResponse update(LoginUser loginUser, Long currentTagId, String newTagName) {
53+
User user = userRepository.findByUsername(loginUser.getUsername())
54+
.orElseThrow(() -> new CoreException(ErrorType.USER_NOT_FOUND));
55+
Tag currentTag = tagRepository.findById(currentTagId)
56+
.orElseThrow(() -> new CoreException(ErrorType.TAG_NOT_FOUND));
57+
UserTag userTag = userTagRepository.findByUserAndTag(user, currentTag)
58+
.orElseThrow(() -> new CoreException(ErrorType.USER_TAG_NOT_FOUND));
59+
Tag newTag = tagRegister.registerTagsFor(newTagName);
60+
61+
validateDuplicateUserTag(user, newTag);
62+
63+
userTagRepository.delete(userTag);
64+
userTagRepository.save(UserTag.create(user, newTag));
65+
66+
return TagResponse.from(newTag);
67+
}
68+
4969
private void validate(User user, Tag tag) {
5070
validateDuplicateUserTag(user, tag);
5171
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: 9 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,11 @@ class UserErrorCodeControllerTest extends ErrorCodeDocumentTest {
1921
TOO_MANY_USER_TAGS, USER_NOT_FOUND);
2022
generateErrorDocs("errorCode/createUserTag", errorCodeDescriptors);
2123
}
24+
25+
@Test
26+
void 유저_태그_수정_에러_코드_문서화() {
27+
List<ErrorCodeDescriptor> errorCodeDescriptors = generateErrorCodeDescriptors(USER_TAG_ALREADY_EXISTS,
28+
USER_NOT_FOUND, TAG_NOT_FOUND, USER_TAG_NOT_FOUND);
29+
generateErrorDocs("errorCode/updateUserTag", errorCodeDescriptors);
30+
}
2231
}

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33
import static com.capturecat.test.api.RestDocsUtil.requestPreprocessor;
44
import static com.capturecat.test.api.RestDocsUtil.responsePreprocessor;
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.Mockito.mock;
89
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
910
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
1011
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
1112
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
13+
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
1214
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
1315
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
1416
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
1517

18+
import java.util.Map;
19+
1620
import org.junit.jupiter.api.BeforeEach;
1721
import org.junit.jupiter.api.Test;
1822
import org.mockito.BDDMockito;
@@ -62,4 +66,29 @@ void setUp() {
6266
fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("태그 ID"),
6367
fieldWithPath("data.name").type(JsonFieldType.STRING).description("태그 이름"))));
6468
}
69+
70+
@Test
71+
void 유저_태그_수정() {
72+
// given
73+
BDDMockito.given(userTagService.update(any(), anyLong(), anyString())).willReturn(new TagResponse(1L, "java"));
74+
75+
// when & then
76+
given()
77+
.header(HttpHeaders.AUTHORIZATION, JwtUtil.BEARER_PREFIX + ACCESS_TOKEN)
78+
.contentType(ContentType.JSON)
79+
.body(Map.of("currentTagId", 1L, "newTagName", "spring"))
80+
.when().patch("/v1/user-tags")
81+
.then().status(HttpStatus.OK)
82+
.apply(document("updateUserTag", requestPreprocessor(), responsePreprocessor(),
83+
requestHeaders(headerWithName(HttpHeaders.AUTHORIZATION).description("유효한 Access 토큰")),
84+
requestFields(
85+
fieldWithPath("currentTagId").type(JsonFieldType.NUMBER).description("수정할 태그 ID"),
86+
fieldWithPath("newTagName").type(JsonFieldType.STRING).description("새로운 태그 이름")
87+
),
88+
responseFields(
89+
fieldWithPath("result").type(JsonFieldType.STRING).description("요청 결과"),
90+
fieldWithPath("data").type(JsonFieldType.OBJECT).description("수정된 태그 정보"),
91+
fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("태그 ID"),
92+
fieldWithPath("data.name").type(JsonFieldType.STRING).description("태그 이름"))));
93+
}
6594
}

0 commit comments

Comments
 (0)