diff --git a/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/controller/AgendaProfileController.java b/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/controller/AgendaProfileController.java index 01d57068a..6b2c34fe8 100644 --- a/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/controller/AgendaProfileController.java +++ b/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/controller/AgendaProfileController.java @@ -1,15 +1,25 @@ package gg.agenda.api.user.agendaprofile.controller; +import javax.validation.Valid; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import gg.agenda.api.user.agendaprofile.controller.request.AgendaProfileChangeReqDto; import gg.agenda.api.user.agendaprofile.controller.response.AgendaProfileDetailsResDto; import gg.agenda.api.user.agendaprofile.service.AgendaProfileFindService; +import gg.agenda.api.user.agendaprofile.service.AgendaProfileModifyService; import gg.auth.UserDto; import gg.auth.argumentresolver.Login; +import gg.utils.exception.user.UserNotFoundException; +import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; @RestController @@ -17,6 +27,8 @@ @RequestMapping("/agenda/profile") public class AgendaProfileController { private final AgendaProfileFindService agendaProfileFindService; + private final AgendaProfileModifyService agendaProfileModifyService; + private static final Logger log = LoggerFactory.getLogger(AgendaProfileController.class); /** * AgendaProfile 상세 조회 API @@ -25,9 +37,25 @@ public class AgendaProfileController { * @return AgendaProfileDetailsResDto 객체와 HTTP 상태 코드를 포함한 ResponseEntity */ @GetMapping - public ResponseEntity getMyAgendaProfile(@Login UserDto user) { + public ResponseEntity getMyAgendaProfile( + @Login @Parameter(hidden = true) UserDto user) { AgendaProfileDetailsResDto agendaProfileDetails = agendaProfileFindService.getAgendaProfileDetails(user); return ResponseEntity.status(HttpStatus.OK).body(agendaProfileDetails); } + + /** + * AgendaProfile 변경 API + * + * @param user 로그인한 사용자 정보 + * @param reqDto 변경할 프로필 정보 + * @return HTTP 상태 코드와 빈 응답 + */ + @PatchMapping + public ResponseEntity modifyAgendaProfile(@Login @Parameter(hidden = true) UserDto user, + @RequestBody @Valid AgendaProfileChangeReqDto reqDto) throws UserNotFoundException { + + agendaProfileModifyService.modifyAgendaProfile(user.getIntraId(), reqDto); + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } } diff --git a/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/controller/request/AgendaProfileChangeReqDto.java b/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/controller/request/AgendaProfileChangeReqDto.java new file mode 100644 index 000000000..d75d57a33 --- /dev/null +++ b/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/controller/request/AgendaProfileChangeReqDto.java @@ -0,0 +1,31 @@ +package gg.agenda.api.user.agendaprofile.controller.request; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.hibernate.validator.constraints.URL; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) +public class AgendaProfileChangeReqDto { + + @NotNull + @NotBlank + @Size(max = 50, message = "userContent의 길이가 허용된 범위를 초과합니다.") + private String userContent; + + @URL + @Size(max = 100, message = "userGithub의 길이가 허용된 범위를 초과합니다.") + private String userGithub; + + @Builder + public AgendaProfileChangeReqDto(String userContent, String userGithub) { + this.userContent = userContent; + this.userGithub = userGithub; + } +} diff --git a/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/service/AgendaProfileModifyService.java b/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/service/AgendaProfileModifyService.java new file mode 100644 index 000000000..c517a7ca4 --- /dev/null +++ b/gg-agenda-api/src/main/java/gg/agenda/api/user/agendaprofile/service/AgendaProfileModifyService.java @@ -0,0 +1,37 @@ +package gg.agenda.api.user.agendaprofile.service; + +import javax.transaction.Transactional; + +import org.springframework.stereotype.Service; + +import gg.agenda.api.user.agendaprofile.controller.request.AgendaProfileChangeReqDto; +import gg.data.agenda.AgendaProfile; +import gg.data.user.User; +import gg.repo.agenda.AgendaProfileRepository; +import gg.repo.user.UserRepository; +import gg.utils.exception.user.UserNotFoundException; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class AgendaProfileModifyService { + + private final UserRepository userRepository; + private final AgendaProfileRepository agendaProfileRepository; + + @Transactional + public void modifyAgendaProfile(String intraId, AgendaProfileChangeReqDto reqDto) throws UserNotFoundException { + // User와 AgendaProfile을 조회 + User user = userRepository.findByIntraId(intraId) + .orElseThrow(() -> new UserNotFoundException()); + + AgendaProfile agendaProfile = agendaProfileRepository.findByUserId(user.getId()) + .orElseThrow(() -> new UserNotFoundException()); + + // 변경된 값들로 업데이트 + agendaProfile.updateProfile(reqDto.getUserContent(), reqDto.getUserGithub()); + + // 변경사항을 저장 + agendaProfileRepository.save(agendaProfile); + } +} diff --git a/gg-agenda-api/src/test/java/gg/agenda/api/user/agendaprofile/AgendaProfileControllerTest.java b/gg-agenda-api/src/test/java/gg/agenda/api/user/agendaprofile/AgendaProfileControllerTest.java index 9fb8af369..de159c97b 100644 --- a/gg-agenda-api/src/test/java/gg/agenda/api/user/agendaprofile/AgendaProfileControllerTest.java +++ b/gg-agenda-api/src/test/java/gg/agenda/api/user/agendaprofile/AgendaProfileControllerTest.java @@ -13,11 +13,13 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import com.fasterxml.jackson.databind.ObjectMapper; import gg.agenda.api.AgendaMockData; +import gg.agenda.api.user.agendaprofile.controller.request.AgendaProfileChangeReqDto; import gg.agenda.api.user.agendaprofile.controller.response.AgendaProfileDetailsResDto; import gg.data.agenda.AgendaProfile; import gg.data.agenda.Ticket; @@ -97,4 +99,104 @@ void testAgendaProfileNotFound() throws Exception { .andExpect(status().isNotFound()); } } + + @Nested + @DisplayName("개인 프로필 정보 변경") + class UpdateAgendaProfile { + + @BeforeEach + void beforeEach() { + user = testDataUtils.createNewUser(); + accessToken = testDataUtils.getLoginAccessTokenFromUser(user); + } + + @Test + @DisplayName("유효한 정보로 개인 프로필을 변경합니다.") + void updateProfileWithValidData() throws Exception { + // Given + AgendaProfile agendaProfile = agendaMockData.createAgendaProfile(user, SEOUL); + Ticket ticket = agendaMockData.createTicket(agendaProfile); + AgendaProfileChangeReqDto requestDto = new AgendaProfileChangeReqDto("Valid user content", + "https://github.com/validUser"); + + String content = objectMapper.writeValueAsString(requestDto); + + // When & Then + mockMvc.perform(patch("/agenda/profile") + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isNoContent()) + .andReturn().getResponse().getContentAsString(); + } + + @Test + @DisplayName("userContent 없이 개인 프로필을 변경합니다.") + void updateProfileWithoutUserContent() throws Exception { + // Given + AgendaProfileChangeReqDto requestDto = new AgendaProfileChangeReqDto("", "https://github.com/validUser"); + + String content = objectMapper.writeValueAsString(requestDto); + + // When & Then + mockMvc.perform(patch("/agenda/profile") + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("잘못된 형식의 userGithub로 개인 프로필을 변경합니다.") + void updateProfileWithInvalidUserGithub() throws Exception { + // Given + AgendaProfileChangeReqDto requestDto = new AgendaProfileChangeReqDto("Valid user content", + "invalidGithubUrl"); + + String content = objectMapper.writeValueAsString(requestDto); + + // When & Then + mockMvc.perform(patch("/agenda/profile") + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("userContent가 허용된 길이를 초과하여 개인 프로필을 변경합니다.") + void updateProfileWithExceededUserContentLength() throws Exception { + // Given + String longContent = "a".repeat(1001); // Assuming the limit is 1000 characters + AgendaProfileChangeReqDto requestDto = new AgendaProfileChangeReqDto(longContent, + "https://github.com/validUser"); + + String content = objectMapper.writeValueAsString(requestDto); + + // When & Then + mockMvc.perform(patch("/agenda/profile") + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("userGithub가 허용된 길이를 초과하여 개인 프로필을 변경합니다.") + void updateProfileWithExceededUserGithubLength() throws Exception { + // Given + String longGithubUrl = "https://github.com/" + "a".repeat(256); // Assuming the limit is 255 characters + AgendaProfileChangeReqDto requestDto = new AgendaProfileChangeReqDto("Valid user content", longGithubUrl); + + String content = objectMapper.writeValueAsString(requestDto); + + // When & Then + mockMvc.perform(patch("/agenda/profile") + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isBadRequest()); + } + } } + diff --git a/gg-data/src/main/java/gg/data/agenda/AgendaProfile.java b/gg-data/src/main/java/gg/data/agenda/AgendaProfile.java index 68cf619d8..710367e6b 100644 --- a/gg-data/src/main/java/gg/data/agenda/AgendaProfile.java +++ b/gg-data/src/main/java/gg/data/agenda/AgendaProfile.java @@ -57,4 +57,13 @@ public AgendaProfile(String content, String githubUrl, Coalition coalition, Loca this.intraId = intraId; this.userId = userId; } + + public void updateProfile(String content, String githubUrl) { + if (content != null && !content.equals(this.content)) { + this.content = content; + } + if (githubUrl != null && !githubUrl.equals(this.githubUrl)) { + this.githubUrl = githubUrl; + } + } }