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..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; @@ -65,6 +67,21 @@ 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)); + 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-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()); + } } } 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(); + } + } }