From d9824ebabee7e5456ea2ec352dd86be090b2b0d5 Mon Sep 17 00:00:00 2001 From: taehyeon Date: Tue, 31 Dec 2024 14:52:17 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20[Feature]=20=EA=B0=9C=EC=9D=B8?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=82=AD=EC=A0=9C=20api=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20#1058?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PrivateScheduleController.java | 8 ++++++++ .../controller/request/PrivateScheduleUpdateReqDto.java | 2 +- .../privateschedule/service/PrivateScheduleService.java | 7 +++++++ .../src/main/java/gg/data/calendar/PrivateSchedule.java | 8 ++++++++ .../src/main/java/gg/data/calendar/PublicSchedule.java | 4 ++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleController.java b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleController.java index d09da761b..f3f8cf294 100644 --- a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleController.java +++ b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleController.java @@ -4,6 +4,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -42,4 +43,11 @@ public ResponseEntity privateScheduleUpdate( privateScheduleUpdateReqDto, id); return ResponseEntity.status(HttpStatus.OK).body(privateScheduleUpdateResDto); } + + @PatchMapping("/{id}") + public ResponseEntity privateScheduleDelete(@Login @Parameter(hidden = true) UserDto userDto, + @PathVariable Long id) { + privateScheduleService.deletePrivateSchedule(userDto, id); + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } } diff --git a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/request/PrivateScheduleUpdateReqDto.java b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/request/PrivateScheduleUpdateReqDto.java index fb0622e95..831cfa503 100644 --- a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/request/PrivateScheduleUpdateReqDto.java +++ b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/controller/request/PrivateScheduleUpdateReqDto.java @@ -48,7 +48,7 @@ public class PrivateScheduleUpdateReqDto { private Long groupId; @Builder - public PrivateScheduleUpdateReqDto(EventTag eventTag, JobTag jobTag, TechTag techTag, String title, String content, + private PrivateScheduleUpdateReqDto(EventTag eventTag, JobTag jobTag, TechTag techTag, String title, String content, String link, ScheduleStatus status, LocalDateTime startTime, LocalDateTime endTime, boolean alarm, Long groupId) { this.eventTag = eventTag; diff --git a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java index f514ccbbe..f3492beab 100644 --- a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java +++ b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java @@ -65,6 +65,13 @@ public PrivateScheduleUpdateResDto updatePrivateSchedule(UserDto userDto, return PrivateScheduleUpdateResDto.toDto(privateSchedule); } + @Transactional + public void deletePrivateSchedule(UserDto userDto, Long privateScheduleId) { + PrivateSchedule privateSchedule = privateScheduleRepository.findById(privateScheduleId) + .orElseThrow(() -> new NotExistException(ErrorCode.PRIVATE_SCHEDULE_NOT_FOUND)); + privateSchedule.delete(); + } + public void validateTimeRange(LocalDateTime startTime, LocalDateTime endTime) { if (endTime.isBefore(startTime)) { throw new InvalidParameterException(ErrorCode.CALENDAR_BEFORE_DATE); diff --git a/gg-data/src/main/java/gg/data/calendar/PrivateSchedule.java b/gg-data/src/main/java/gg/data/calendar/PrivateSchedule.java index 630077b8e..a9d5e59b6 100644 --- a/gg-data/src/main/java/gg/data/calendar/PrivateSchedule.java +++ b/gg-data/src/main/java/gg/data/calendar/PrivateSchedule.java @@ -14,6 +14,7 @@ import javax.persistence.ManyToOne; import gg.data.BaseTimeEntity; +import gg.data.calendar.type.DetailClassification; import gg.data.calendar.type.EventTag; import gg.data.calendar.type.JobTag; import gg.data.calendar.type.ScheduleStatus; @@ -65,4 +66,11 @@ public void update(EventTag eventTag, JobTag jobTag, TechTag techTag, String tit this.publicSchedule.update(publicSchedule.getClassification(), eventTag, jobTag, techTag, title, content, link, startTime, endTime, status); } + + public void delete() { + this.status = ScheduleStatus.DELETE; + if (this.publicSchedule.getClassification() == DetailClassification.PRIVATE_SCHEDULE) { + publicSchedule.delete(); + } + } } diff --git a/gg-data/src/main/java/gg/data/calendar/PublicSchedule.java b/gg-data/src/main/java/gg/data/calendar/PublicSchedule.java index f0eaaa193..2d3d616cb 100644 --- a/gg-data/src/main/java/gg/data/calendar/PublicSchedule.java +++ b/gg-data/src/main/java/gg/data/calendar/PublicSchedule.java @@ -100,5 +100,9 @@ public void update(DetailClassification classification, EventTag eventTag, JobTa this.endTime = endTime; this.status = status; } + + public void delete() { + this.status = ScheduleStatus.DELETE; + } } From 0812ca15788e4cebc2b6baf0cd00af60c395b201 Mon Sep 17 00:00:00 2001 From: taehyeon Date: Tue, 31 Dec 2024 15:13:10 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20[Feature]=20=EA=B0=9C=EC=9D=B8?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=82=AD=EC=A0=9C=20api=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80=20#1058?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/PrivateScheduleService.java | 10 ++++++++++ .../src/main/java/gg/utils/exception/ErrorCode.java | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java index f3492beab..4a52808ca 100644 --- a/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java +++ b/gg-calendar-api/src/main/java/gg/calendar/api/user/schedule/privateschedule/service/PrivateScheduleService.java @@ -12,12 +12,14 @@ import gg.data.calendar.PrivateSchedule; import gg.data.calendar.PublicSchedule; import gg.data.calendar.ScheduleGroup; +import gg.data.calendar.type.ScheduleStatus; import gg.data.user.User; import gg.repo.calendar.PrivateScheduleRepository; import gg.repo.calendar.PublicScheduleRepository; import gg.repo.calendar.ScheduleGroupRepository; import gg.repo.user.UserRepository; import gg.utils.exception.ErrorCode; +import gg.utils.exception.custom.DuplicationException; import gg.utils.exception.custom.ForbiddenException; import gg.utils.exception.custom.InvalidParameterException; import gg.utils.exception.custom.NotExistException; @@ -69,9 +71,17 @@ public PrivateScheduleUpdateResDto updatePrivateSchedule(UserDto userDto, public void deletePrivateSchedule(UserDto userDto, Long privateScheduleId) { PrivateSchedule privateSchedule = privateScheduleRepository.findById(privateScheduleId) .orElseThrow(() -> new NotExistException(ErrorCode.PRIVATE_SCHEDULE_NOT_FOUND)); + validateDeletion(privateSchedule.getStatus()); + validateAuthor(userDto.getIntraId(), privateSchedule.getPublicSchedule().getAuthor()); privateSchedule.delete(); } + public void validateDeletion(ScheduleStatus status) { + if (status == ScheduleStatus.DELETE) { + throw new DuplicationException(ErrorCode.CALENDAR_ALREADY_DELETE); + } + } + public void validateTimeRange(LocalDateTime startTime, LocalDateTime endTime) { if (endTime.isBefore(startTime)) { throw new InvalidParameterException(ErrorCode.CALENDAR_BEFORE_DATE); diff --git a/gg-utils/src/main/java/gg/utils/exception/ErrorCode.java b/gg-utils/src/main/java/gg/utils/exception/ErrorCode.java index 017c51a21..06d72a2cf 100644 --- a/gg-utils/src/main/java/gg/utils/exception/ErrorCode.java +++ b/gg-utils/src/main/java/gg/utils/exception/ErrorCode.java @@ -243,7 +243,8 @@ public enum ErrorCode { CALENDAR_AUTHOR_NOT_MATCH(403, "CA205", "잘못된 사용자입니다."), PRIVATE_SCHEDULE_NOT_FOUND(404, "CA101", "개인 일정을 찾을 수 없습니다."), PUBLIC_SCHEDULE_NOT_FOUND(404, "CA102", "공유 일정을 찾을 수 없습니다."), - SCHEDULE_GROUP_NOT_FOUND(404, "CA103", "스캐줄 그룹을 찾을 수 없습니다."); + SCHEDULE_GROUP_NOT_FOUND(404, "CA103", "스캐줄 그룹을 찾을 수 없습니다."), + CALENDAR_ALREADY_DELETE(409, "CA104", "이미 삭제된 일정입니다."); private final int status; private final String errCode; From dbe693b33219894fe34c4dfeff7d90688964b759 Mon Sep 17 00:00:00 2001 From: taehyeon Date: Tue, 31 Dec 2024 16:02:52 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=AA=20[Test]=20=EA=B0=9C=EC=9D=B8?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20=EC=82=AD=EC=A0=9C=20api=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20#1058?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PrivateScheduleMockData.java | 6 +- .../PrivateScheduleControllerTest.java | 140 +++++++++++++++--- 2 files changed, 119 insertions(+), 27 deletions(-) diff --git a/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/PrivateScheduleMockData.java b/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/PrivateScheduleMockData.java index 7d95b56e9..a28e1eb76 100644 --- a/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/PrivateScheduleMockData.java +++ b/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/PrivateScheduleMockData.java @@ -48,9 +48,9 @@ public ScheduleGroup createScheduleGroup(User user) { return scheduleGroupRepository.save(scheduleGroup); } - public PrivateSchedule createPrivateSchedule(PublicSchedule publicSchedule, ScheduleGroup scheduleGroup) { - PrivateSchedule privateSchedule = new PrivateSchedule(scheduleGroup.getUser(), publicSchedule, false, - scheduleGroup.getId()); + public PrivateSchedule createPrivateSchedule(User user, PublicSchedule publicSchedule, Long scheduleGroupId) { + PrivateSchedule privateSchedule = new PrivateSchedule(user, publicSchedule, false, + scheduleGroupId); return privateScheduleRepository.save(privateSchedule); } } diff --git a/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleControllerTest.java b/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleControllerTest.java index 5e8513be6..90469bc68 100644 --- a/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleControllerTest.java +++ b/gg-calendar-api/src/test/java/gg/calendar/api/user/schedule/privateschedule/controller/PrivateScheduleControllerTest.java @@ -37,6 +37,7 @@ @Slf4j @IntegrationTest @AutoConfigureMockMvc +@Transactional public class PrivateScheduleControllerTest { @Autowired private MockMvc mockMvc; @@ -66,8 +67,7 @@ void setUp() { @DisplayName("PrivateSchedule 생성하기") class CreatePrivateSchedule { @Test - @Transactional - @DisplayName("성공") + @DisplayName("성공 201") void success() throws Exception { //given ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); @@ -100,7 +100,6 @@ void success() throws Exception { } @Test - @Transactional @DisplayName("일정 그룹이 없는 경우 404") void noGroup() throws Exception { //given @@ -126,7 +125,6 @@ void noGroup() throws Exception { } @Test - @Transactional @DisplayName("시작 날짜보다 끝나는 날짜가 빠른 경우 400") void endTimeBeforeStartTime() throws Exception { //given @@ -156,14 +154,13 @@ void endTimeBeforeStartTime() throws Exception { @DisplayName("PrivateSchedule 수정하기") class UpdatePrivateSchedule { @Test - @Transactional - @DisplayName("성공") + @DisplayName("성공 200") void success() throws Exception { //given ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule(user.getIntraId()); - PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(publicSchedule, - scheduleGroup); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); PrivateScheduleUpdateReqDto reqDto = PrivateScheduleUpdateReqDto.builder() .eventTag(null) .techTag(null) @@ -185,21 +182,18 @@ void success() throws Exception { .andExpect(status().isOk()); //then PrivateSchedule updated = privateScheduleRepository.findById(privateSchedule.getId()).orElseThrow(); - Assertions.assertThat(privateSchedule.getGroupId()).isEqualTo(updated.getGroupId()); - Assertions.assertThat(privateSchedule.isAlarm()).isEqualTo(updated.isAlarm()); - Assertions.assertThat(privateSchedule.getGroupId()).isEqualTo(updated.getGroupId()); - Assertions.assertThat(privateSchedule.getPublicSchedule()).isEqualTo(updated.getPublicSchedule()); + Assertions.assertThat(reqDto.getGroupId()).isEqualTo(updated.getGroupId()); + Assertions.assertThat(reqDto.isAlarm()).isEqualTo(updated.isAlarm()); } @Test - @Transactional @DisplayName("종료 날짜가 시작 날짜보다 빠른 경우 400") void endTimeBeforeStartTime() throws Exception { //given ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule(user.getIntraId()); - PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(publicSchedule, - scheduleGroup); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); PrivateScheduleUpdateReqDto reqDto = PrivateScheduleUpdateReqDto.builder() .eventTag(null) .techTag(null) @@ -222,14 +216,13 @@ void endTimeBeforeStartTime() throws Exception { } @Test - @Transactional @DisplayName("작성자가 아닌 사람이 일정을 수정 하려는 경우 403") void notMatchAuthor() throws Exception { //given ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule("notMatchAuthor"); - PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(publicSchedule, - scheduleGroup); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); PrivateScheduleUpdateReqDto reqDto = PrivateScheduleUpdateReqDto.builder() .eventTag(null) .techTag(null) @@ -243,7 +236,7 @@ void notMatchAuthor() throws Exception { .endTime(LocalDateTime.now().plusDays(1)) .groupId(scheduleGroup.getId()) .build(); - //when + //when&then mockMvc.perform(put("/calendar/private/" + privateSchedule.getId()) .header("Authorization", "Bearer " + accessToken) .contentType(MediaType.APPLICATION_JSON) @@ -252,14 +245,13 @@ void notMatchAuthor() throws Exception { } @Test - @Transactional @DisplayName("일정이 없는 경우 404") void noSchedule() throws Exception { //given ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); - PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule("notMatchAuthor"); - PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(publicSchedule, - scheduleGroup); + PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule(user.getIntraId()); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); PrivateScheduleUpdateReqDto reqDto = PrivateScheduleUpdateReqDto.builder() .eventTag(null) .techTag(null) @@ -273,12 +265,112 @@ void noSchedule() throws Exception { .endTime(LocalDateTime.now().plusDays(1)) .groupId(scheduleGroup.getId()) .build(); - //when + //when&then mockMvc.perform(put("/calendar/private/" + privateSchedule.getId() + 123411243) .header("Authorization", "Bearer " + accessToken) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(reqDto))) .andExpect(status().isNotFound()); } + + @Test + @DisplayName("일정 그룹이 없는 경우 404") + void noScheduleGroup() throws Exception { + //given + ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); + PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule(user.getIntraId()); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); + PrivateScheduleUpdateReqDto reqDto = PrivateScheduleUpdateReqDto.builder() + .eventTag(null) + .techTag(null) + .jobTag(null) + .alarm(false) + .title("123") + .content("") + .link(null) + .status(ScheduleStatus.ACTIVATE) + .startTime(LocalDateTime.now()) + .endTime(LocalDateTime.now().plusDays(1)) + .groupId(0L) + .build(); + //when&then + mockMvc.perform(put("/calendar/private/" + privateSchedule.getId()) + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqDto))) + .andExpect(status().isNotFound()); + } + } + + @Nested + @DisplayName("PrivateSchedule 삭제하기") + class DeletePrivateSchedule { + @Test + @DisplayName("성공 204") + void success() throws Exception { + //given + ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); + PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule(user.getIntraId()); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); + //when + mockMvc.perform(patch("/calendar/private/" + privateSchedule.getId()) + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + //then + Assertions.assertThat(privateSchedule.getStatus()).isEqualTo(ScheduleStatus.DELETE); + } + + @Test + @DisplayName("작성자가 아닌 사람이 삭제하는 경우 403") + void notMatchAuthor() throws Exception { + //given + ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); + PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule("author"); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); + //when + mockMvc.perform(patch("/calendar/private/" + privateSchedule.getId()) + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + //then + Assertions.assertThat(privateSchedule.getStatus()).isEqualTo(ScheduleStatus.ACTIVATE); + } + + @Test + @DisplayName("없는 일정인 경우 404") + void notSchedule() throws Exception { + //given + ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); + PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule(user.getIntraId()); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); + //when + mockMvc.perform(patch("/calendar/private/" + privateSchedule.getId() + 1234) + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()); + //then + Assertions.assertThat(privateSchedule.getStatus()).isEqualTo(ScheduleStatus.ACTIVATE); + } + + @Test + @DisplayName("이미 삭제된 일정인 경우 409") + void alreadyDelete() throws Exception { + //given + ScheduleGroup scheduleGroup = privateScheduleMockData.createScheduleGroup(user); + PublicSchedule publicSchedule = privateScheduleMockData.createPublicSchedule(user.getIntraId()); + PrivateSchedule privateSchedule = privateScheduleMockData.createPrivateSchedule(user, publicSchedule, + scheduleGroup.getId()); + privateSchedule.delete(); + //when&then + mockMvc.perform(patch("/calendar/private/" + privateSchedule.getId()) + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isConflict()); + } } }