From 4381081e4f96a8ad7f847c711e31bcbe9f206eeb Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Fri, 28 Jul 2023 20:40:04 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=ED=96=89=EC=82=AC(Event)=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- backend/emm-sale/src/docs/asciidoc/index.adoc | 15 +- .../com/emmsale/GlobalControllerAdvice.java | 23 ++ .../java/com/emmsale/event/api/EventApi.java | 11 + .../event/application/EventService.java | 64 ++++- .../application/dto/EventCreateRequest.java | 35 +++ .../java/com/emmsale/event/domain/Event.java | 11 +- .../event/exception/EventExceptionType.java | 8 + .../tag/application/dto/TagRequest.java | 16 ++ .../com/emmsale/event/api/EventApiTest.java | 231 ++++++++++++++++++ .../event/application/EventServiceTest.java | 174 +++++++++---- 10 files changed, 532 insertions(+), 56 deletions(-) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventCreateRequest.java create mode 100644 backend/emm-sale/src/main/java/com/emmsale/tag/application/dto/TagRequest.java diff --git a/backend/emm-sale/src/docs/asciidoc/index.adoc b/backend/emm-sale/src/docs/asciidoc/index.adoc index d3f71ca08..aa396aa4d 100644 --- a/backend/emm-sale/src/docs/asciidoc/index.adoc +++ b/backend/emm-sale/src/docs/asciidoc/index.adoc @@ -93,7 +93,6 @@ include::{snippets}/find-profile/http-response.adoc[] .HTTP response 설명 include::{snippets}/find-profile/response-fields.adoc[] - == Event === `GET` : 행사 상세정보 조회 @@ -140,6 +139,20 @@ include::{snippets}/find-participants/http-response.adoc[] .HTTP response 설명 include::{snippets}/find-participants/response-fields.adoc[] +=== `POST` : 이벤트 생성 + +.HTTP request +include::{snippets}/find-participants/http-request.adoc[] + +.HTTP request 설명 +include::{snippets}/find-participants/request-fields.adoc[] + +.HTTP response +include::{snippets}/find-participants/http-response.adoc[] + +.HTTP response 설명 +include::{snippets}/find-participants/response-fields.adoc[] + == Comment === `GET` : 댓글 모두 조회 diff --git a/backend/emm-sale/src/main/java/com/emmsale/GlobalControllerAdvice.java b/backend/emm-sale/src/main/java/com/emmsale/GlobalControllerAdvice.java index ce5921eee..16c3835db 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/GlobalControllerAdvice.java +++ b/backend/emm-sale/src/main/java/com/emmsale/GlobalControllerAdvice.java @@ -2,8 +2,13 @@ import com.emmsale.base.BaseException; import com.emmsale.base.BaseExceptionType; +import java.time.format.DateTimeParseException; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -18,6 +23,24 @@ public ResponseEntity handleException(final BaseException e) return new ResponseEntity<>(ExceptionResponse.from(e), type.httpStatus()); } + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationException( + final MethodArgumentNotValidException e) { + final String message = e.getBindingResult().getAllErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.joining("\n")); + log.warn("[WARN] MESSAGE: {}", message); + return new ResponseEntity<>(new ExceptionResponse(message), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(DateTimeParseException.class) + public ResponseEntity handleDateTimeParseException( + final DateTimeParseException e) { + final String message = "DateTime 입력 형식이 올바르지 않습니다."; + log.warn("[WARN] MESSAGE: " + message); + return new ResponseEntity<>(new ExceptionResponse(message), HttpStatus.BAD_REQUEST); + } + static class ExceptionResponse { private final String message; diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java index 55401d11d..edf7699a7 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java @@ -4,6 +4,7 @@ import static java.net.URI.create; import com.emmsale.event.application.EventService; +import com.emmsale.event.application.dto.EventCreateRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventParticipateRequest; import com.emmsale.event.application.dto.EventResponse; @@ -11,7 +12,9 @@ import com.emmsale.member.domain.Member; import java.time.LocalDate; import java.util.List; +import javax.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -19,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @RestController @@ -62,4 +66,11 @@ public ResponseEntity> findEvents(@RequestParam final int ye @RequestParam(required = false) final String status) { return ResponseEntity.ok(eventService.findEvents(LocalDate.now(), year, month, tag, status)); } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public EventDetailResponse addEvent( + @RequestBody @Valid final EventCreateRequest request) { + return eventService.addEvent(request); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index 47b2e8477..fe9fb1058 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -9,6 +9,7 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toUnmodifiableList; +import com.emmsale.event.application.dto.EventCreateRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.application.dto.ParticipantResponse; @@ -26,6 +27,8 @@ import com.emmsale.tag.domain.TagRepository; import com.emmsale.tag.exception.TagException; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import lombok.RequiredArgsConstructor; @@ -46,6 +49,12 @@ public class EventService { private final EventTagRepository eventTagRepository; private final TagRepository tagRepository; + private static void validateMemberNotAllowed(final Long memberId, final Member member) { + if (member.isNotMe(memberId)) { + throw new EventException(EventExceptionType.FORBIDDEN_PARTICIPATE_EVENT); + } + } + @Transactional(readOnly = true) public EventDetailResponse findEvent(final Long id) { final Event event = eventRepository.findById(id) @@ -63,17 +72,11 @@ public Long participate(final Long eventId, final Long memberId, final Member me return participant.getId(); } - private static void validateMemberNotAllowed(final Long memberId, final Member member) { - if (member.isNotMe(memberId)) { - throw new EventException(EventExceptionType.FORBIDDEN_PARTICIPATE_EVENT); - } - } - @Transactional(readOnly = true) public List findEvents(final LocalDate nowDate, final int year, final int month, final String tagName, final String statusName) { validateYearAndMonth(year, month); - List events = filterEventsByTag(tagName); + final List events = filterEventsByTag(tagName); final EnumMap> sortAndGroupByStatus = groupByEventStatus(nowDate, events, year, month); @@ -102,7 +105,7 @@ private void validateYearAndMonth(final int year, final int month) { private List filterEventsByTag(final String tagName) { if (isExistTagName(tagName)) { - Tag tag = tagRepository.findByName(tagName) + final Tag tag = tagRepository.findByName(tagName) .orElseThrow(() -> new TagException(NOT_FOUND_TAG)); return eventTagRepository.findEventTagsByTag(tag) @@ -132,22 +135,22 @@ private EnumMap> groupByEventStatus(final LocalDate now private boolean isOverlapToMonth(final int year, final int month, final LocalDate eventStart, final LocalDate eventEnd) { - LocalDate monthStart = LocalDate.of(year, month, 1); - LocalDate monthEnd = LocalDate.of(year, month, monthStart.lengthOfMonth()); + final LocalDate monthStart = LocalDate.of(year, month, 1); + final LocalDate monthEnd = LocalDate.of(year, month, monthStart.lengthOfMonth()); return (isBeforeOrEquals(eventStart, monthEnd) && isBeforeOrEquals(monthStart, eventEnd)) || (isBeforeOrEquals(monthStart, eventStart) && isBeforeOrEquals(eventStart, monthEnd)) || (isBeforeOrEquals(monthStart, eventEnd) && isBeforeOrEquals(eventEnd, monthEnd)); } - private boolean isBeforeOrEquals(LocalDate criteria, LocalDate comparison) { + private boolean isBeforeOrEquals(final LocalDate criteria, final LocalDate comparison) { return criteria.isBefore(comparison) || criteria.isEqual(comparison); } private List filterEventResponsesByStatus(final String statusName, final EnumMap> sortAndGroupByEventStatus) { if (isExistStatusName(statusName)) { - EventStatus status = EventStatus.from(statusName); + final EventStatus status = EventStatus.from(statusName); return EventResponse.makeEventResponsesByStatus(status, sortAndGroupByEventStatus.get(status)); } @@ -158,5 +161,42 @@ private boolean isExistStatusName(final String statusName) { return statusName != null; } + public EventDetailResponse addEvent(final EventCreateRequest request) { + validateStartBeforeOrEqualEndDateTime(request.getStartDateTime(), request.getEndDateTime()); + + final Event event = getPersistentEvent(request); + + final List tags = getPersistTags(request); + + for (final Tag tag : tags) { + event.addEventTag(tag); + } + + return EventDetailResponse.from(event); + } + + private void validateStartBeforeOrEqualEndDateTime(final LocalDateTime startDateTime, + final LocalDateTime endDateTime) { + if (startDateTime.isAfter(endDateTime)) { + throw new EventException(EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME); + } + } + + private List getPersistTags(final EventCreateRequest request) { + if (request.getTags() == null || request.getTags().isEmpty()) { + return new ArrayList<>(); + } + return request.getTags().stream() + .map(tagRequest -> tagRepository.findByName(tagRequest.getName()) + .orElseThrow(() -> new EventException(EventExceptionType.NOT_FOUND_TAG))) + .collect(toList()); + } + + private Event getPersistentEvent(final EventCreateRequest request) { + final Event event = new Event(request.getName(), request.getLocation(), + request.getStartDateTime(), request.getEndDateTime(), request.getInformationUrl()); + + return eventRepository.save(event); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventCreateRequest.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventCreateRequest.java new file mode 100644 index 000000000..85fd7e9fe --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventCreateRequest.java @@ -0,0 +1,35 @@ +package com.emmsale.event.application.dto; + +import com.emmsale.tag.application.dto.TagRequest; +import java.time.LocalDateTime; +import java.util.List; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +@RequiredArgsConstructor +@Getter +public class EventCreateRequest { + + private static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + + @NotBlank(message = "행사의 이름을 입력해 주세요.") + private final String name; + @NotBlank(message = "행사의 장소를 입력해 주세요.") + private final String location; + @NotBlank(message = "행사의 상세 URL을 입력해 주세요.") + @Pattern(regexp = "(http.?://).*", message = "http:// 혹은 https://로 시작하는 주소를 입력해 주세요.") + private final String informationUrl; + + @DateTimeFormat(pattern = DATE_TIME_FORMAT) + @NotNull(message = "행사의 시작 일시를 입력해 주세요.") + private final LocalDateTime startDateTime; + @DateTimeFormat(pattern = DATE_TIME_FORMAT) + @NotNull(message = "행사의 종료 일시를 입력해 주세요.") + private final LocalDateTime endDateTime; + + private final List tags; +} diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java index e459f9a45..bcbc2585a 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java @@ -7,6 +7,7 @@ import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; import com.emmsale.member.domain.Member; +import com.emmsale.tag.domain.Tag; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; @@ -39,7 +40,7 @@ public class Event extends BaseEntity { private LocalDateTime endDate; @Column(nullable = false) private String informationUrl; - @OneToMany(mappedBy = "event") + @OneToMany(mappedBy = "event", cascade = CascadeType.PERSIST) private List tags = new ArrayList<>(); @OneToMany(mappedBy = "event") private List comments; @@ -66,6 +67,12 @@ public Participant addParticipant(final Member member) { return participant; } + public EventTag addEventTag(final Tag tag) { + final EventTag eventTag = new EventTag(this, tag); + tags.add(eventTag); + return eventTag; + } + public void validateAlreadyParticipate(final Member member) { if (isAlreadyParticipate(member)) { throw new EventException(EventExceptionType.ALREADY_PARTICIPATED); @@ -77,7 +84,7 @@ private boolean isAlreadyParticipate(final Member member) { .anyMatch(participant -> participant.isSameMember(member)); } - public EventStatus calculateEventStatus(LocalDate now) { + public EventStatus calculateEventStatus(final LocalDate now) { if (now.isBefore(startDate.toLocalDate())) { return EventStatus.UPCOMING; } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/exception/EventExceptionType.java b/backend/emm-sale/src/main/java/com/emmsale/event/exception/EventExceptionType.java index 215098b3a..e2feb57ac 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/exception/EventExceptionType.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/exception/EventExceptionType.java @@ -21,6 +21,14 @@ public enum EventExceptionType implements BaseExceptionType { INVALID_MONTH( HttpStatus.BAD_REQUEST, "월은 1에서 12 사이여야 합니다." + ), + NOT_FOUND_TAG( + HttpStatus.NOT_FOUND, + "해당하는 태그를 찾을 수 없습니다." + ), + START_DATE_TIME_AFTER_END_DATE_TIME( + HttpStatus.BAD_REQUEST, + "행사의 시작 일시가 종료 일시 이후일 수 없습니다." ); private final HttpStatus httpStatus; diff --git a/backend/emm-sale/src/main/java/com/emmsale/tag/application/dto/TagRequest.java b/backend/emm-sale/src/main/java/com/emmsale/tag/application/dto/TagRequest.java new file mode 100644 index 000000000..64ba6e911 --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/tag/application/dto/TagRequest.java @@ -0,0 +1,16 @@ +package com.emmsale.tag.application.dto; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public class TagRequest { + + private final String name; + + @JsonCreator + public TagRequest(@JsonProperty final String name) { + this.name = name; + } +} diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java index 290affd32..e3c25367e 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java @@ -12,20 +12,33 @@ import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.emmsale.event.EventFixture; import com.emmsale.event.application.EventService; +import com.emmsale.event.application.dto.EventCreateRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventParticipateRequest; import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.application.dto.ParticipantResponse; +import com.emmsale.event.domain.Event; +import com.emmsale.event.domain.EventStatus; import com.emmsale.helper.MockMvcTestHelper; +import com.emmsale.tag.TagFixture; +import com.emmsale.tag.application.dto.TagRequest; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; +import java.util.stream.Collectors; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; @@ -33,6 +46,7 @@ import org.springframework.restdocs.payload.RequestFieldsSnippet; import org.springframework.restdocs.payload.ResponseFieldsSnippet; import org.springframework.restdocs.request.RequestParametersSnippet; +import org.springframework.test.web.servlet.ResultActions; @WebMvcTest(EventApi.class) class EventApiTest extends MockMvcTestHelper { @@ -183,4 +197,221 @@ void findParticipants() throws Exception { .andExpect(status().isOk()) .andDo(document("find-participants", responseFields)); } + + @Nested + class AddEvent { + + @Test + @DisplayName("이벤트를 성공적으로 추가하면 201, CREATED 를 반환한다.") + void addEventTest() throws Exception { + //given + final Event event = EventFixture.인프콘_2023(); + + final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + .map(tag -> new TagRequest(tag.getName())) + .collect(Collectors.toList()); + + final EventCreateRequest request = new EventCreateRequest( + event.getName(), + event.getLocation(), + event.getInformationUrl(), + event.getStartDate(), + event.getEndDate(), + tags + ); + + final EventDetailResponse response = new EventDetailResponse(1L, request.getName(), + request.getInformationUrl(), + request.getStartDateTime(), request.getEndDateTime(), request.getLocation(), + EventStatus.IN_PROGRESS.getValue(), + tags.stream().map(TagRequest::getName).collect(Collectors.toList())); + + when(eventService.addEvent(any())).thenReturn(response); + + final RequestFieldsSnippet requestFields = requestFields( + fieldWithPath("name").type(JsonFieldType.STRING).description("행사(Event) 이름"), + fieldWithPath("location").type(JsonFieldType.STRING).description("행사(Event) 장소"), + fieldWithPath("startDateTime").type(JsonFieldType.STRING).description("행사(Event) 시작일시"), + fieldWithPath("endDateTime").type(JsonFieldType.STRING).description("행사(Event) 종료일시"), + fieldWithPath("informationUrl").type(JsonFieldType.STRING) + .description("행사(Event) 상세 정보 URL"), + fieldWithPath("tags[].name").type(JsonFieldType.STRING).description("연관 태그명") + ); + + final ResponseFieldsSnippet responseFields = responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("행사(Event) id"), + fieldWithPath("name").type(JsonFieldType.STRING).description("행사(Event) 이름"), + fieldWithPath("informationUrl").type(JsonFieldType.STRING) + .description("행사(Event) 상세 정보 URL"), + fieldWithPath("startDate").type(JsonFieldType.STRING).description("행사(Event) 시작일시"), + fieldWithPath("endDate").type(JsonFieldType.STRING).description("행사(Event) 종료일시"), + fieldWithPath("location").type(JsonFieldType.STRING).description("행사(Event) 장소"), + fieldWithPath("status").type(JsonFieldType.STRING).description("행사(Event) 진행 상태"), + fieldWithPath("tags[]").type(JsonFieldType.ARRAY).description("행사(Event) 연관 태그 목록") + ); + + //when + final ResultActions result = mockMvc.perform(post("/events") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))); + + //then + result.andExpect(status().isCreated()) + .andDo(print()) + .andDo(document("find-participants", requestFields, responseFields)); + } + + @ParameterizedTest + @NullSource + @EmptySource + @DisplayName("이름에 빈 값이 들어올 경우 400 BAD_REQUEST를 반환한다.") + void addEventWithEmptyNameTest(final String eventName) throws Exception { + //given + final Event event = EventFixture.인프콘_2023(); + + final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + .map(tag -> new TagRequest(tag.getName())) + .collect(Collectors.toList()); + + final EventCreateRequest request = new EventCreateRequest( + eventName, + event.getLocation(), + event.getInformationUrl(), + event.getStartDate(), + event.getEndDate(), + tags + ); + + //when + final ResultActions result = mockMvc.perform(post("/events") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))); + + //then + result.andExpect(status().isBadRequest()); + } + + @ParameterizedTest + @NullSource + @EmptySource + @DisplayName("장소에 빈 값이 들어올 경우 400 BAD_REQUEST를 반환한다.") + void addEventWithEmptyLocationTest(final String eventLocation) throws Exception { + //given + final Event event = EventFixture.인프콘_2023(); + + final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + .map(tag -> new TagRequest(tag.getName())) + .collect(Collectors.toList()); + + final EventCreateRequest request = new EventCreateRequest( + event.getName(), + eventLocation, + event.getInformationUrl(), + event.getStartDate(), + event.getEndDate(), + tags + ); + + //when + final ResultActions result = mockMvc.perform(post("/events") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))); + + //then + result.andExpect(status().isBadRequest()); + } + + @ParameterizedTest + @ValueSource(strings = {"httpexample.com", "http:example.com", "http:/example.com", + "httpsexample.com", "https:example.com", "https:/example.com"}) + @NullSource + @DisplayName("상세 URL에 http:// 혹은 https://로 시작하지 않는 값이 들어올 경우 400 BAD_REQUEST를 반환한다.") + void addEventWithInvalidInformationUrlTest(final String informationUrl) throws Exception { + //given + final Event event = EventFixture.인프콘_2023(); + + final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + .map(tag -> new TagRequest(tag.getName())) + .collect(Collectors.toList()); + + final EventCreateRequest request = new EventCreateRequest( + event.getName(), + event.getLocation(), + informationUrl, + event.getStartDate(), + event.getEndDate(), + tags + ); + + //when + final ResultActions result = mockMvc.perform(post("/events") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))); + + //then + result.andExpect(status().isBadRequest()); + } + + @ParameterizedTest + @ValueSource(strings = {"23-01-01T12:00:00", "2023-1-01T12:00:00", "2023-01-1T12:00:00", + "2023-01-01T2:00:00", "2023-01-01T12:0:00", "2023-01-01T12:00:0"}) + @NullSource + @DisplayName("시작 일시에 null 혹은 다른 형식의 일시 값이 들어올 경우 400 BAD_REQUEST를 반환한다.") + void addEventWithUnformattedStartDateTimeTest(final String startDateTime) throws Exception { + //given + final Event event = EventFixture.인프콘_2023(); + + final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + .map(tag -> new TagRequest(tag.getName())) + .collect(Collectors.toList()); + + final String request = "{" + + "\"name\":\"인프콘 2023\"," + + "\"location\":\"코엑스\"," + + "\"informationUrl\":\"https://~~~\"," + + "\"startDateTime\":" + startDateTime + "," + + "\"endDateTime\":\"2023-01-02T12:00:00\"" + + ",\"tags\":[{\"name\":\"백엔드\"},{\"name\":\"안드로이드\"}]" + + "}"; + + //when + final ResultActions result = mockMvc.perform(post("/events") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(request)); + + //then + result.andExpect(status().isBadRequest()); + } + + @ParameterizedTest + @ValueSource(strings = {"23-01-02T12:00:00", "2023-1-02T12:00:00", "2023-01-2T12:00:00", + "2023-01-02T2:00:00", "2023-01-02T12:0:00", "2023-01-02T12:00:0"}) + @NullSource + @DisplayName("종료 일시에 null 혹은 다른 형식의 일시 값이 들어올 경우 400 BAD_REQUEST를 반환한다.") + void addEventWithUnformattedEndDateTimeTest(final String endDateTime) throws Exception { + //given + final Event event = EventFixture.인프콘_2023(); + + final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + .map(tag -> new TagRequest(tag.getName())) + .collect(Collectors.toList()); + + final String request = "{" + + "\"name\":\"인프콘 2023\"," + + "\"location\":\"코엑스\"," + + "\"informationUrl\":\"https://~~~\"," + + "\"startDateTime\":\"2023-01-01T12:00:00\"" + + "\"endDateTime\":" + endDateTime + "," + + ",\"tags\":[{\"name\":\"백엔드\"},{\"name\":\"안드로이드\"}]" + + "}"; + + //when + final ResultActions result = mockMvc.perform(post("/events") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(request)); + + //then + result.andExpect(status().isBadRequest()); + } + } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index 8ce947774..60a713d1d 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -13,7 +13,11 @@ import static com.emmsale.tag.TagFixture.프론트엔드; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import com.emmsale.event.application.dto.EventCreateRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.application.dto.ParticipantResponse; @@ -26,11 +30,13 @@ import com.emmsale.helper.ServiceIntegrationTestHelper; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; +import com.emmsale.tag.application.dto.TagRequest; import com.emmsale.tag.domain.Tag; import com.emmsale.tag.domain.TagRepository; import com.emmsale.tag.exception.TagException; import com.emmsale.tag.exception.TagExceptionType; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.BeforeEach; @@ -43,8 +49,6 @@ class EventServiceTest extends ServiceIntegrationTestHelper { - @Autowired - private MemberRepository memberRepository; private static final EventResponse 인프콘_2023 = new EventResponse(null, "인프콘 2023", null, null, List.of(), "진행 중"); private static final EventResponse 웹_컨퍼런스 = new EventResponse(null, "웹 컨퍼런스", null, null, @@ -57,7 +61,8 @@ class EventServiceTest extends ServiceIntegrationTestHelper { private static final EventResponse 모바일_컨퍼런스 = new EventResponse(null, "모바일 컨퍼런스", null, null, List.of(), "진행 예정"); private static final LocalDate TODAY = LocalDate.of(2023, 7, 21); - + @Autowired + private MemberRepository memberRepository; @Autowired private EventService eventService; @Autowired @@ -69,17 +74,17 @@ class EventServiceTest extends ServiceIntegrationTestHelper { @BeforeEach void init() { - Tag 백엔드 = tagRepository.save(백엔드()); - Tag 프론트엔드 = tagRepository.save(프론트엔드()); - Tag 안드로이드 = tagRepository.save(안드로이드()); - Tag IOS = tagRepository.save(IOS()); - Tag AI = tagRepository.save(AI()); - - Event 인프콘_2023 = eventRepository.save(인프콘_2023()); - Event AI_컨퍼런스 = eventRepository.save(AI_컨퍼런스()); - Event 모바일_컨퍼런스 = eventRepository.save(모바일_컨퍼런스()); - Event 안드로이드_컨퍼런스 = eventRepository.save(안드로이드_컨퍼런스()); - Event 웹_컨퍼런스 = eventRepository.save(웹_컨퍼런스()); + final Tag 백엔드 = tagRepository.save(백엔드()); + final Tag 프론트엔드 = tagRepository.save(프론트엔드()); + final Tag 안드로이드 = tagRepository.save(안드로이드()); + final Tag IOS = tagRepository.save(IOS()); + final Tag AI = tagRepository.save(AI()); + + final Event 인프콘_2023 = eventRepository.save(인프콘_2023()); + final Event AI_컨퍼런스 = eventRepository.save(AI_컨퍼런스()); + final Event 모바일_컨퍼런스 = eventRepository.save(모바일_컨퍼런스()); + final Event 안드로이드_컨퍼런스 = eventRepository.save(안드로이드_컨퍼런스()); + final Event 웹_컨퍼런스 = eventRepository.save(웹_컨퍼런스()); eventTagRepository.saveAll(List.of( new EventTag(인프콘_2023, 백엔드), new EventTag(인프콘_2023, 프론트엔드), new EventTag(인프콘_2023, 안드로이드), @@ -89,6 +94,32 @@ void init() { ); } + @Test + @DisplayName("event의 id로 참여자 목록을 조회할 수 있다.") + void findParticipants() { + // given + final Event 인프콘 = eventRepository.save(eventFixture()); + final Member 멤버1 = memberRepository.save(new Member(123L, "image1.com", "멤버1")); + final Member 멤버2 = memberRepository.save(new Member(124L, "image2.com", "멤버2")); + + final Long 멤버1_참가자_ID = eventService.participate(인프콘.getId(), 멤버1.getId(), 멤버1); + final Long 멤버2_참가자_ID = eventService.participate(인프콘.getId(), 멤버2.getId(), 멤버2); + + //when + final List actual = eventService.findParticipants(인프콘.getId()); + + final List expected = List.of( + new ParticipantResponse(멤버1_참가자_ID, 멤버1.getId(), 멤버1.getName(), 멤버1.getImageUrl(), + 멤버1.getDescription()), + new ParticipantResponse(멤버2_참가자_ID, 멤버2.getId(), 멤버2.getName(), 멤버2.getImageUrl(), + 멤버2.getDescription()) + ); + //then + assertThat(actual) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrderElementsOf(expected); + } + @Nested @DisplayName("id로 이벤트를 조회할 수 있다.") class findEventTest { @@ -215,7 +246,7 @@ void findEvents_2023_6() { @ParameterizedTest @ValueSource(ints = {2014, 0, -1}) @DisplayName("유효하지 않은 값이 연도 값으로 들어오면 예외를 반환한다.") - void findEvents_year_fail(int year) { + void findEvents_year_fail(final int year) { // given, when final ThrowingCallable actual = () -> eventService.findEvents(TODAY, year, 7, null, null); @@ -228,7 +259,7 @@ void findEvents_year_fail(int year) { @ParameterizedTest @ValueSource(ints = {0, -1, 13, 14}) @DisplayName("유효하지 않은 값이 월 값으로 들어오면 예외를 반환한다.") - void findEvents_month_fail(int month) { + void findEvents_month_fail(final int month) { // given, when final ThrowingCallable actual = () -> eventService.findEvents(TODAY, 2023, month, null, null); @@ -296,31 +327,92 @@ void findEvents_status_filter_fail() { .isInstanceOf(EventException.class) .hasMessage(EventExceptionType.INVALID_STATUS.errorMessage()); } - } - - @Test - @DisplayName("event의 id로 참여자 목록을 조회할 수 있다.") - void findParticipants() { - // given - final Event 인프콘 = eventRepository.save(eventFixture()); - final Member 멤버1 = memberRepository.save(new Member(123L, "image1.com", "멤버1")); - final Member 멤버2 = memberRepository.save(new Member(124L, "image2.com", "멤버2")); - - final Long 멤버1_참가자_ID = eventService.participate(인프콘.getId(), 멤버1.getId(), 멤버1); - final Long 멤버2_참가자_ID = eventService.participate(인프콘.getId(), 멤버2.getId(), 멤버2); - //when - final List actual = eventService.findParticipants(인프콘.getId()); - - final List expected = List.of( - new ParticipantResponse(멤버1_참가자_ID, 멤버1.getId(), 멤버1.getName(), 멤버1.getImageUrl(), - 멤버1.getDescription()), - new ParticipantResponse(멤버2_참가자_ID, 멤버2.getId(), 멤버2.getName(), 멤버2.getImageUrl(), - 멤버2.getDescription()) - ); - //then - assertThat(actual) - .usingRecursiveFieldByFieldElementComparator() - .containsExactlyInAnyOrderElementsOf(expected); + @Nested + class AddEvent { + + private final LocalDateTime beforeDateTime = LocalDateTime.now(); + private final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); + + @Test + @DisplayName("이벤트를 성공적으로 저장한다.") + void addEventTest() { + //given + final String eventName = "이름"; + final String eventLocation = "장소"; + final String eventInformationUrl = "https://naver.com"; + final LocalDateTime startDateTime = beforeDateTime; + final LocalDateTime endDateTime = afterDateTime; + final List tagRequests = List.of( + new TagRequest(백엔드().getName()), + new TagRequest(안드로이드().getName()) + ); + + final EventCreateRequest request = new EventCreateRequest(eventName, eventLocation, + eventInformationUrl, startDateTime, endDateTime, tagRequests); + + //when + final EventDetailResponse response = eventService.addEvent(request); + + //then + assertAll( + () -> assertEquals(eventName, response.getName()), + () -> assertEquals(eventLocation, response.getLocation()), + () -> assertEquals(eventInformationUrl, response.getInformationUrl()), + () -> assertEquals(tagRequests.size(), response.getTags().size()) + ); + } + + @Test + @DisplayName("행사 시작 일시가 행사 종료 일시 이후일 경우 EventException이 발생한다.") + void addEventWithStartDateTimeAfterBeforeDateTimeTest() { + //given + final String eventName = "이름"; + final String eventLocation = "장소"; + final String eventInformationUrl = "https://naver.com"; + final LocalDateTime startDateTime = afterDateTime; + final LocalDateTime endDatetime = beforeDateTime; + final List tagRequests = List.of( + new TagRequest(백엔드().getName()), + new TagRequest(안드로이드().getName()) + ); + + final EventCreateRequest request = new EventCreateRequest(eventName, eventLocation, + eventInformationUrl, startDateTime, endDatetime, tagRequests); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventService.addEvent(request)); + + assertEquals(exception.exceptionType(), + EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME); + } + + @Test + @DisplayName("Tag가 존재하지 않을 경우 EventException이 발생한다.") + void addEventWithNotExistTagTest() { + //given + final String eventName = "이름"; + final String eventLocation = "장소"; + final String eventInformationUrl = "https://naver.com"; + final LocalDateTime startDateTime = beforeDateTime; + final LocalDateTime endDatetime = afterDateTime; + final List tagRequests = List.of( + new TagRequest(백엔드().getName()), + new TagRequest(안드로이드().getName()), + new TagRequest("존재하지 않는 태그") + ); + + final EventCreateRequest request = new EventCreateRequest(eventName, eventLocation, + eventInformationUrl, startDateTime, endDatetime, tagRequests); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventService.addEvent(request)); + + assertEquals(exception.exceptionType(), + EventExceptionType.NOT_FOUND_TAG); + } + } } } From 9288b1661ea3200ae0c4ae5112d96fe0d6ffc74f Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 10:22:27 +0900 Subject: [PATCH 02/15] =?UTF-8?q?refactor:=20EventCreateRequest=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../src/main/java/com/emmsale/event/api/EventApi.java | 5 +++-- .../com/emmsale/event/application/EventService.java | 8 ++++---- ...EventCreateRequest.java => EventDetailRequest.java} | 2 +- .../test/java/com/emmsale/event/api/EventApiTest.java | 10 +++++----- .../emmsale/event/application/EventServiceTest.java | 8 ++++---- 5 files changed, 17 insertions(+), 16 deletions(-) rename backend/emm-sale/src/main/java/com/emmsale/event/application/dto/{EventCreateRequest.java => EventDetailRequest.java} (97%) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java index edf7699a7..d935a8e8a 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java @@ -4,7 +4,7 @@ import static java.net.URI.create; import com.emmsale.event.application.EventService; -import com.emmsale.event.application.dto.EventCreateRequest; +import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventParticipateRequest; import com.emmsale.event.application.dto.EventResponse; @@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -70,7 +71,7 @@ public ResponseEntity> findEvents(@RequestParam final int ye @PostMapping @ResponseStatus(HttpStatus.CREATED) public EventDetailResponse addEvent( - @RequestBody @Valid final EventCreateRequest request) { + @RequestBody @Valid final EventDetailRequest request) { return eventService.addEvent(request); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index fe9fb1058..7ba1765aa 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -9,7 +9,7 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toUnmodifiableList; -import com.emmsale.event.application.dto.EventCreateRequest; +import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.application.dto.ParticipantResponse; @@ -161,7 +161,7 @@ private boolean isExistStatusName(final String statusName) { return statusName != null; } - public EventDetailResponse addEvent(final EventCreateRequest request) { + public EventDetailResponse addEvent(final EventDetailRequest request) { validateStartBeforeOrEqualEndDateTime(request.getStartDateTime(), request.getEndDateTime()); final Event event = getPersistentEvent(request); @@ -182,7 +182,7 @@ private void validateStartBeforeOrEqualEndDateTime(final LocalDateTime startDate } } - private List getPersistTags(final EventCreateRequest request) { + private List getPersistTags(final EventDetailRequest request) { if (request.getTags() == null || request.getTags().isEmpty()) { return new ArrayList<>(); } @@ -193,7 +193,7 @@ private List getPersistTags(final EventCreateRequest request) { .collect(toList()); } - private Event getPersistentEvent(final EventCreateRequest request) { + private Event getPersistentEvent(final EventDetailRequest request) { final Event event = new Event(request.getName(), request.getLocation(), request.getStartDateTime(), request.getEndDateTime(), request.getInformationUrl()); diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventCreateRequest.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailRequest.java similarity index 97% rename from backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventCreateRequest.java rename to backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailRequest.java index 85fd7e9fe..0f229ebab 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventCreateRequest.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailRequest.java @@ -12,7 +12,7 @@ @RequiredArgsConstructor @Getter -public class EventCreateRequest { +public class EventDetailRequest { private static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java index e3c25367e..cd3de8529 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java @@ -18,7 +18,7 @@ import com.emmsale.event.EventFixture; import com.emmsale.event.application.EventService; -import com.emmsale.event.application.dto.EventCreateRequest; +import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventParticipateRequest; import com.emmsale.event.application.dto.EventResponse; @@ -211,7 +211,7 @@ void addEventTest() throws Exception { .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); - final EventCreateRequest request = new EventCreateRequest( + final EventDetailRequest request = new EventDetailRequest( event.getName(), event.getLocation(), event.getInformationUrl(), @@ -273,7 +273,7 @@ void addEventWithEmptyNameTest(final String eventName) throws Exception { .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); - final EventCreateRequest request = new EventCreateRequest( + final EventDetailRequest request = new EventDetailRequest( eventName, event.getLocation(), event.getInformationUrl(), @@ -303,7 +303,7 @@ void addEventWithEmptyLocationTest(final String eventLocation) throws Exception .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); - final EventCreateRequest request = new EventCreateRequest( + final EventDetailRequest request = new EventDetailRequest( event.getName(), eventLocation, event.getInformationUrl(), @@ -334,7 +334,7 @@ void addEventWithInvalidInformationUrlTest(final String informationUrl) throws E .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); - final EventCreateRequest request = new EventCreateRequest( + final EventDetailRequest request = new EventDetailRequest( event.getName(), event.getLocation(), informationUrl, diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index 60a713d1d..d77b23031 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; -import com.emmsale.event.application.dto.EventCreateRequest; +import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.application.dto.ParticipantResponse; @@ -348,7 +348,7 @@ void addEventTest() { new TagRequest(안드로이드().getName()) ); - final EventCreateRequest request = new EventCreateRequest(eventName, eventLocation, + final EventDetailRequest request = new EventDetailRequest(eventName, eventLocation, eventInformationUrl, startDateTime, endDateTime, tagRequests); //when @@ -377,7 +377,7 @@ void addEventWithStartDateTimeAfterBeforeDateTimeTest() { new TagRequest(안드로이드().getName()) ); - final EventCreateRequest request = new EventCreateRequest(eventName, eventLocation, + final EventDetailRequest request = new EventDetailRequest(eventName, eventLocation, eventInformationUrl, startDateTime, endDatetime, tagRequests); //when & then @@ -403,7 +403,7 @@ void addEventWithNotExistTagTest() { new TagRequest("존재하지 않는 태그") ); - final EventCreateRequest request = new EventCreateRequest(eventName, eventLocation, + final EventDetailRequest request = new EventDetailRequest(eventName, eventLocation, eventInformationUrl, startDateTime, endDatetime, tagRequests); //when & then From 5203ae1f9e7c471f4daecccff6d560936bf6228c Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 10:36:20 +0900 Subject: [PATCH 03/15] =?UTF-8?q?refactor:=20getPersistTags=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../com/emmsale/event/application/EventService.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index 7ba1765aa..0c8d80c38 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -23,6 +23,7 @@ import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; import com.emmsale.member.domain.Member; +import com.emmsale.tag.application.dto.TagRequest; import com.emmsale.tag.domain.Tag; import com.emmsale.tag.domain.TagRepository; import com.emmsale.tag.exception.TagException; @@ -166,7 +167,7 @@ public EventDetailResponse addEvent(final EventDetailRequest request) { final Event event = getPersistentEvent(request); - final List tags = getPersistTags(request); + final List tags = getPersistTags(request.getTags()); for (final Tag tag : tags) { event.addEventTag(tag); @@ -182,13 +183,13 @@ private void validateStartBeforeOrEqualEndDateTime(final LocalDateTime startDate } } - private List getPersistTags(final EventDetailRequest request) { - if (request.getTags() == null || request.getTags().isEmpty()) { + private List getPersistTags(final List tags) { + if (tags == null || tags.isEmpty()) { return new ArrayList<>(); } - return request.getTags().stream() - .map(tagRequest -> tagRepository.findByName(tagRequest.getName()) + return tags.stream() + .map(tag -> tagRepository.findByName(tag.getName()) .orElseThrow(() -> new EventException(EventExceptionType.NOT_FOUND_TAG))) .collect(toList()); } From 9d4823cbf7c6976cb562c7376364ab0ea77e9c39 Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 12:34:53 +0900 Subject: [PATCH 04/15] =?UTF-8?q?fix:=20add-event=20snippet=EB=AA=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- backend/emm-sale/src/docs/asciidoc/index.adoc | 8 ++++---- .../src/test/java/com/emmsale/event/api/EventApiTest.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/emm-sale/src/docs/asciidoc/index.adoc b/backend/emm-sale/src/docs/asciidoc/index.adoc index aa396aa4d..2ceba42ba 100644 --- a/backend/emm-sale/src/docs/asciidoc/index.adoc +++ b/backend/emm-sale/src/docs/asciidoc/index.adoc @@ -142,16 +142,16 @@ include::{snippets}/find-participants/response-fields.adoc[] === `POST` : 이벤트 생성 .HTTP request -include::{snippets}/find-participants/http-request.adoc[] +include::{snippets}/add-event/http-request.adoc[] .HTTP request 설명 -include::{snippets}/find-participants/request-fields.adoc[] +include::{snippets}/add-event/request-fields.adoc[] .HTTP response -include::{snippets}/find-participants/http-response.adoc[] +include::{snippets}/add-event/http-response.adoc[] .HTTP response 설명 -include::{snippets}/find-participants/response-fields.adoc[] +include::{snippets}/add-event/response-fields.adoc[] == Comment diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java index cd3de8529..30a5db382 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java @@ -258,7 +258,7 @@ void addEventTest() throws Exception { //then result.andExpect(status().isCreated()) .andDo(print()) - .andDo(document("find-participants", requestFields, responseFields)); + .andDo(document("add-event", requestFields, responseFields)); } @ParameterizedTest From 0d9f73deff395ba2e9b0f8287fa79bf6bdf55ede Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 12:48:12 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20event=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../java/com/emmsale/event/api/EventApi.java | 7 ++ .../event/application/EventService.java | 16 ++++ .../java/com/emmsale/event/domain/Event.java | 25 ++++- .../com/emmsale/event/domain/EventTag.java | 4 +- .../domain/repository/EventTagRepository.java | 1 + .../com/emmsale/event/api/EventApiTest.java | 61 ++++++++++++ .../event/application/EventServiceTest.java | 96 +++++++++++++++++++ .../com/emmsale/event/domain/EventTest.java | 39 ++++++++ 8 files changed, 246 insertions(+), 3 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java index d935a8e8a..90d690688 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java @@ -74,4 +74,11 @@ public EventDetailResponse addEvent( @RequestBody @Valid final EventDetailRequest request) { return eventService.addEvent(request); } + + @PutMapping("/{event-id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public EventDetailResponse updateEvent(@PathVariable(name = "event-id") final Long eventId, + @RequestBody @Valid final EventDetailRequest request) { + return eventService.updateEvent(eventId, request); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index 0c8d80c38..d5e1071ed 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -183,6 +183,22 @@ private void validateStartBeforeOrEqualEndDateTime(final LocalDateTime startDate } } + public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequest request) { + validateStartBeforeOrEqualEndDateTime(request.getStartDateTime(), request.getEndDateTime()); + + final Event event = eventRepository.findById(eventId) + .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); + + final List tags = getPersistTags(request.getTags()); + + eventTagRepository.deleteAllByEventId(eventId); + + final Event updatedEvent = event.updateEventContent(request.getName(), request.getLocation(), + request.getStartDateTime(), request.getEndDateTime(), request.getInformationUrl(), tags); + + return EventDetailResponse.from(updatedEvent); + } + private List getPersistTags(final List tags) { if (tags == null || tags.isEmpty()) { return new ArrayList<>(); diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java index bcbc2585a..90b9fc855 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java @@ -15,6 +15,7 @@ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -40,7 +41,7 @@ public class Event extends BaseEntity { private LocalDateTime endDate; @Column(nullable = false) private String informationUrl; - @OneToMany(mappedBy = "event", cascade = CascadeType.PERSIST) + @OneToMany(mappedBy = "event", fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) private List tags = new ArrayList<>(); @OneToMany(mappedBy = "event") private List comments; @@ -93,4 +94,26 @@ public EventStatus calculateEventStatus(final LocalDate now) { } return EventStatus.IN_PROGRESS; } + + public Event updateEventContent( + final String name, + final String location, + final LocalDateTime startDate, + final LocalDateTime endDate, + final String informationUrl, + final List tags + ) { + this.name = name; + this.location = location; + this.startDate = startDate; + this.endDate = endDate; + this.informationUrl = informationUrl; + this.tags = new ArrayList<>(); + + for (final Tag tag : tags) { + addEventTag(tag); + } + + return this; + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventTag.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventTag.java index 572b1ec46..ecb3371e3 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventTag.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventTag.java @@ -23,11 +23,11 @@ public class EventTag { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "event_id", nullable = false) private Event event; - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "tag_id", nullable = false) private Tag tag; - public EventTag(Event event, Tag tag) { + public EventTag(final Event event, final Tag tag) { this.event = event; this.tag = tag; } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/repository/EventTagRepository.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/repository/EventTagRepository.java index 5149843e2..8c063f7f7 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/repository/EventTagRepository.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/repository/EventTagRepository.java @@ -9,4 +9,5 @@ public interface EventTagRepository extends JpaRepository { List findEventTagsByTag(Tag tag); + void deleteAllByEventId(Long eventId); } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java index 30a5db382..acdfeb44d 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java @@ -12,6 +12,7 @@ import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -198,6 +199,66 @@ void findParticipants() throws Exception { .andDo(document("find-participants", responseFields)); } + @Test + @DisplayName("이벤트를 성공적으로 업데이트하면 204, NO_CONTENT를 반환한다.") + void updateEventTest() throws Exception { + //given + final long eventId = 1L; + final Event event = EventFixture.인프콘_2023(); + + final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + .map(tag -> new TagRequest(tag.getName())) + .collect(Collectors.toList()); + + final EventDetailRequest request = new EventDetailRequest( + event.getName(), + event.getLocation(), + event.getInformationUrl(), + event.getStartDate(), + event.getEndDate(), + tags + ); + + final EventDetailResponse response = new EventDetailResponse(eventId, request.getName(), + request.getInformationUrl(), request.getStartDateTime(), request.getEndDateTime(), + request.getLocation(), EventStatus.IN_PROGRESS.getValue(), + tags.stream().map(TagRequest::getName).collect(Collectors.toList())); + + when(eventService.updateEvent(any(), any())).thenReturn(response); + + final RequestFieldsSnippet requestFields = requestFields( + fieldWithPath("name").type(JsonFieldType.STRING).description("행사(Event) 이름"), + fieldWithPath("location").type(JsonFieldType.STRING).description("행사(Event) 장소"), + fieldWithPath("startDateTime").type(JsonFieldType.STRING).description("행사(Event) 시작일시"), + fieldWithPath("endDateTime").type(JsonFieldType.STRING).description("행사(Event) 종료일시"), + fieldWithPath("informationUrl").type(JsonFieldType.STRING) + .description("행사(Event) 상세 정보 URL"), + fieldWithPath("tags[].name").type(JsonFieldType.STRING).description("연관 태그명") + ); + + final ResponseFieldsSnippet responseFields = responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("행사(Event) id"), + fieldWithPath("name").type(JsonFieldType.STRING).description("행사(Event) 이름"), + fieldWithPath("informationUrl").type(JsonFieldType.STRING) + .description("행사(Event) 상세 정보 URL"), + fieldWithPath("startDate").type(JsonFieldType.STRING).description("행사(Event) 시작일시"), + fieldWithPath("endDate").type(JsonFieldType.STRING).description("행사(Event) 종료일시"), + fieldWithPath("location").type(JsonFieldType.STRING).description("행사(Event) 장소"), + fieldWithPath("status").type(JsonFieldType.STRING).description("행사(Event) 진행 상태"), + fieldWithPath("tags[]").type(JsonFieldType.ARRAY).description("행사(Event) 연관 태그 목록") + ); + + //when + final ResultActions result = mockMvc.perform(put("/events/" + eventId) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(request))); + + //then + result.andExpect(status().isNoContent()) + .andDo(print()) + .andDo(document("update-event", requestFields, responseFields)); + } + @Nested class AddEvent { diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index d77b23031..b5b29fb82 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -414,5 +414,101 @@ void addEventWithNotExistTagTest() { EventExceptionType.NOT_FOUND_TAG); } } + + @Nested + class UpdateEvent { + + private final LocalDateTime beforeDateTime = LocalDateTime.now(); + private final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); + + @Test + @DisplayName("이벤트를 성공적으로 업데이트한다.") + void updateEventTest() { + //given + final String newName = "새로운 이름"; + final String newLocation = "새로운 장소"; + final LocalDateTime newStartDateTime = beforeDateTime; + final LocalDateTime newEndDateTime = afterDateTime; + final String newInformationUrl = "https://새로운-상세-URL.com"; + final List newTagRequests = List.of( + new TagRequest(IOS().getName()), + new TagRequest(AI().getName()) + ); + + final EventDetailRequest updateRequest = new EventDetailRequest(newName, newLocation, + newInformationUrl, newStartDateTime, newEndDateTime, newTagRequests); + + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when + eventService.updateEvent(eventId, updateRequest); + final Event updatedEvent = eventRepository.findById(eventId).get(); + + //then + assertAll( + () -> assertEquals(newName, updatedEvent.getName()), + () -> assertEquals(newLocation, updatedEvent.getLocation()), + () -> assertEquals(newStartDateTime, updatedEvent.getStartDate()), + () -> assertEquals(newEndDateTime, updatedEvent.getEndDate()), + () -> assertEquals(newInformationUrl, updatedEvent.getInformationUrl()), + () -> assertEquals(newTagRequests.size(), updatedEvent.getTags().size()) + ); + } + + @Test + @DisplayName("행사 시작 일시가 행사 종료 일시 이후일 경우 EventException이 발생한다.") + void updateEventWithStartDateTimeAfterBeforeDateTimeTest() { + //given + final String newName = "새로운 이름"; + final String newLocation = "새로운 장소"; + final LocalDateTime newStartDateTime = afterDateTime; + final LocalDateTime newEndDateTime = beforeDateTime; + final String newInformationUrl = "https://새로운-상세-URL.com"; + final List newTagRequests = List.of( + new TagRequest(IOS().getName()), + new TagRequest(AI().getName()) + ); + + final EventDetailRequest updateRequest = new EventDetailRequest(newName, newLocation, + newInformationUrl, newStartDateTime, newEndDateTime, newTagRequests); + + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventService.updateEvent(eventId, updateRequest)); + + assertEquals(exception.exceptionType(), + EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME); + } + + @Test + @DisplayName("Tag가 존재하지 않을 경우 EventException이 발생한다.") + void updateEventWithNotExistTagTest() { + //given + final String newName = "새로운 이름"; + final String newLocation = "새로운 장소"; + final LocalDateTime newStartDateTime = beforeDateTime; + final LocalDateTime newEndDateTime = afterDateTime; + final String newInformationUrl = "https://새로운-상세-URL.com"; + final List newTagRequests = List.of( + new TagRequest("존재하지 않는 태그") + ); + + final EventDetailRequest updateRequest = new EventDetailRequest(newName, newLocation, + newInformationUrl, newStartDateTime, newEndDateTime, newTagRequests); + + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventService.updateEvent(eventId, updateRequest)); + + assertEquals(exception.exceptionType(), EventExceptionType.NOT_FOUND_TAG); + } + } } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java index 082d562c7..242792325 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java @@ -2,11 +2,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.emmsale.event.EventFixture; import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; import com.emmsale.member.domain.Member; +import com.emmsale.tag.TagFixture; +import com.emmsale.tag.domain.Tag; +import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; import org.junit.jupiter.api.DisplayName; @@ -50,5 +55,39 @@ void fail_alreadyContains() { .isInstanceOf(EventException.class) .hasMessage(EventExceptionType.ALREADY_PARTICIPATED.errorMessage()); } + + @Test + @DisplayName("Event의 name, location, startDate, endDate, informationUrl, tags를 업데이트할 수 있다.") + void updateEventContentTest() { + //given + final String newName = "새로운 이름"; + final String newLocation = "새로운 장소"; + final LocalDateTime newStartDateTime = LocalDateTime.now(); + final LocalDateTime newEndDateTime = newStartDateTime.plusDays(1); + final String newInformationUrl = "https://새로운-상세-URL.com"; + final List newTags = List.of(TagFixture.IOS(), TagFixture.AI()); + + final Event event = EventFixture.인프콘_2023(); + + //when + final Event updatedEvent = event.updateEventContent( + newName, + newLocation, + newStartDateTime, + newEndDateTime, + newInformationUrl, + newTags + ); + + //then + assertAll( + () -> assertEquals(newName, updatedEvent.getName()), + () -> assertEquals(newLocation, updatedEvent.getLocation()), + () -> assertEquals(newStartDateTime, updatedEvent.getStartDate()), + () -> assertEquals(newEndDateTime, updatedEvent.getEndDate()), + () -> assertEquals(newInformationUrl, updatedEvent.getInformationUrl()), + () -> assertEquals(newTags.size(), event.getTags().size()) + ); + } } } From 0a30e2b5d3f1ab7f943e89447bc1bdad2ef9669c Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 12:57:10 +0900 Subject: [PATCH 06/15] =?UTF-8?q?test:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../emmsale/event/application/EventServiceTest.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index b5b29fb82..11b263e8d 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -353,13 +353,16 @@ void addEventTest() { //when final EventDetailResponse response = eventService.addEvent(request); + final Event savedEvent = eventRepository.findById(response.getId()).get(); //then assertAll( - () -> assertEquals(eventName, response.getName()), - () -> assertEquals(eventLocation, response.getLocation()), - () -> assertEquals(eventInformationUrl, response.getInformationUrl()), - () -> assertEquals(tagRequests.size(), response.getTags().size()) + () -> assertEquals(eventName, savedEvent.getName()), + () -> assertEquals(eventLocation, savedEvent.getLocation()), + () -> assertEquals(eventInformationUrl, savedEvent.getInformationUrl()), + () -> assertEquals(startDateTime, savedEvent.getStartDate()), + () -> assertEquals(endDateTime, savedEvent.getEndDate()), + () -> assertEquals(tagRequests.size(), savedEvent.getTags().size()) ); } From e642a349c13b3d5c9932dd416bf3091bac54ad20 Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 13:02:11 +0900 Subject: [PATCH 07/15] =?UTF-8?q?refactor:=20event=20validate=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20Event=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=95=88=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../com/emmsale/event/application/EventService.java | 12 ------------ .../main/java/com/emmsale/event/domain/Event.java | 11 +++++++++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index d5e1071ed..3f80ccfe5 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -28,7 +28,6 @@ import com.emmsale.tag.domain.TagRepository; import com.emmsale.tag.exception.TagException; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; @@ -163,8 +162,6 @@ private boolean isExistStatusName(final String statusName) { } public EventDetailResponse addEvent(final EventDetailRequest request) { - validateStartBeforeOrEqualEndDateTime(request.getStartDateTime(), request.getEndDateTime()); - final Event event = getPersistentEvent(request); final List tags = getPersistTags(request.getTags()); @@ -176,16 +173,7 @@ public EventDetailResponse addEvent(final EventDetailRequest request) { return EventDetailResponse.from(event); } - private void validateStartBeforeOrEqualEndDateTime(final LocalDateTime startDateTime, - final LocalDateTime endDateTime) { - if (startDateTime.isAfter(endDateTime)) { - throw new EventException(EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME); - } - } - public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequest request) { - validateStartBeforeOrEqualEndDateTime(request.getStartDateTime(), request.getEndDateTime()); - final Event event = eventRepository.findById(eventId) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java index 90b9fc855..375e9b00a 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java @@ -55,6 +55,8 @@ public Event( final LocalDateTime endDate, final String informationUrl ) { + validateStartBeforeOrEqualEndDateTime(startDate, endDate); + this.name = name; this.location = location; this.startDate = startDate; @@ -103,6 +105,8 @@ public Event updateEventContent( final String informationUrl, final List tags ) { + validateStartBeforeOrEqualEndDateTime(startDate, endDate); + this.name = name; this.location = location; this.startDate = startDate; @@ -116,4 +120,11 @@ public Event updateEventContent( return this; } + + private void validateStartBeforeOrEqualEndDateTime(final LocalDateTime startDateTime, + final LocalDateTime endDateTime) { + if (startDateTime.isAfter(endDateTime)) { + throw new EventException(EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME); + } + } } From 6ad193c77c98b84a7d4c54695f0fecc0c1b9f39f Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 13:05:19 +0900 Subject: [PATCH 08/15] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../com/emmsale/event/application/EventService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index 3f80ccfe5..a1cf77da1 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -162,9 +162,9 @@ private boolean isExistStatusName(final String statusName) { } public EventDetailResponse addEvent(final EventDetailRequest request) { - final Event event = getPersistentEvent(request); + final Event event = saveNewEvent(request); - final List tags = getPersistTags(request.getTags()); + final List tags = findAllPersistTagsOrElseThrow(request.getTags()); for (final Tag tag : tags) { event.addEventTag(tag); @@ -177,7 +177,7 @@ public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequ final Event event = eventRepository.findById(eventId) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); - final List tags = getPersistTags(request.getTags()); + final List tags = findAllPersistTagsOrElseThrow(request.getTags()); eventTagRepository.deleteAllByEventId(eventId); @@ -187,7 +187,7 @@ public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequ return EventDetailResponse.from(updatedEvent); } - private List getPersistTags(final List tags) { + private List findAllPersistTagsOrElseThrow(final List tags) { if (tags == null || tags.isEmpty()) { return new ArrayList<>(); } @@ -198,7 +198,7 @@ private List getPersistTags(final List tags) { .collect(toList()); } - private Event getPersistentEvent(final EventDetailRequest request) { + private Event saveNewEvent(final EventDetailRequest request) { final Event event = new Event(request.getName(), request.getLocation(), request.getStartDateTime(), request.getEndDateTime(), request.getInformationUrl()); From 8a4ea2f192bdf12ecf4be41bc39947ab3314543d Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 13:08:36 +0900 Subject: [PATCH 09/15] =?UTF-8?q?docs:=20index.adoc=EC=97=90=20snippet=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- backend/emm-sale/src/docs/asciidoc/index.adoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backend/emm-sale/src/docs/asciidoc/index.adoc b/backend/emm-sale/src/docs/asciidoc/index.adoc index 2ceba42ba..01c43f049 100644 --- a/backend/emm-sale/src/docs/asciidoc/index.adoc +++ b/backend/emm-sale/src/docs/asciidoc/index.adoc @@ -153,6 +153,20 @@ include::{snippets}/add-event/http-response.adoc[] .HTTP response 설명 include::{snippets}/add-event/response-fields.adoc[] +=== `PUT` : 이벤트 업데이트 + +.HTTP request +include::{snippets}/update-event/http-request.adoc[] + +.HTTP request 설명 +include::{snippets}/update-event/request-fields.adoc[] + +.HTTP response +include::{snippets}/update-event/http-response.adoc[] + +.HTTP response 설명 +include::{snippets}/update-event/response-fields.adoc[] + == Comment === `GET` : 댓글 모두 조회 From f90c67790177fff572ec2f4171f2e756780189d5 Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 14:45:05 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- backend/emm-sale/src/docs/asciidoc/index.adoc | 11 +++++ .../java/com/emmsale/event/api/EventApi.java | 7 ++++ .../event/application/EventService.java | 9 +++++ .../com/emmsale/event/api/EventApiTest.java | 40 +++++++++++++++++++ .../event/application/EventServiceTest.java | 33 +++++++++++++++ 5 files changed, 100 insertions(+) diff --git a/backend/emm-sale/src/docs/asciidoc/index.adoc b/backend/emm-sale/src/docs/asciidoc/index.adoc index 01c43f049..2ad126f8e 100644 --- a/backend/emm-sale/src/docs/asciidoc/index.adoc +++ b/backend/emm-sale/src/docs/asciidoc/index.adoc @@ -167,6 +167,17 @@ include::{snippets}/update-event/http-response.adoc[] .HTTP response 설명 include::{snippets}/update-event/response-fields.adoc[] +=== `PUT` : 이벤트 삭제 + +.HTTP request +include::{snippets}/delete-event/http-request.adoc[] + +.HTTP response +include::{snippets}/delete-event/http-response.adoc[] + +.HTTP response 설명 +include::{snippets}/delete-event/response-fields.adoc[] + == Comment === `GET` : 댓글 모두 조회 diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java index 90d690688..1034ed834 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java @@ -16,6 +16,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -81,4 +82,10 @@ public EventDetailResponse updateEvent(@PathVariable(name = "event-id") final Lo @RequestBody @Valid final EventDetailRequest request) { return eventService.updateEvent(eventId, request); } + + @DeleteMapping("/{event-id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public EventDetailResponse deleteEvent(@PathVariable(name = "event-id") final Long eventId) { + return eventService.deleteEvent(eventId); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index a1cf77da1..ed31a68f2 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -187,6 +187,15 @@ public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequ return EventDetailResponse.from(updatedEvent); } + public EventDetailResponse deleteEvent(final Long eventId) { + final Event event = eventRepository.findById(eventId) + .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); + + eventRepository.deleteById(eventId); + + return EventDetailResponse.from(event); + } + private List findAllPersistTagsOrElseThrow(final List tags) { if (tags == null || tags.isEmpty()) { return new ArrayList<>(); diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java index acdfeb44d..7addadef2 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java @@ -10,6 +10,7 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; @@ -29,10 +30,12 @@ import com.emmsale.helper.MockMvcTestHelper; import com.emmsale.tag.TagFixture; import com.emmsale.tag.application.dto.TagRequest; +import com.emmsale.tag.domain.Tag; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -259,6 +262,43 @@ void updateEventTest() throws Exception { .andDo(document("update-event", requestFields, responseFields)); } + @Test + @DisplayName("이벤트를 성공적으로 삭제하면 204, NO_CONTENT를 반환한다.") + void deleteEventTest() throws Exception { + //given + final long eventId = 1L; + final Event event = EventFixture.인프콘_2023(); + + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) + .map(Tag::getName).collect(Collectors.toList()); + + final EventDetailResponse response = new EventDetailResponse(eventId, event.getName(), + event.getInformationUrl(), event.getStartDate(), event.getEndDate(), + event.getLocation(), EventStatus.IN_PROGRESS.getValue(), tags); + + when(eventService.deleteEvent(eventId)).thenReturn(response); + + final ResponseFieldsSnippet responseFields = responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("행사(Event) id"), + fieldWithPath("name").type(JsonFieldType.STRING).description("행사(Event) 이름"), + fieldWithPath("informationUrl").type(JsonFieldType.STRING) + .description("행사(Event) 상세 정보 URL"), + fieldWithPath("startDate").type(JsonFieldType.STRING).description("행사(Event) 시작일시"), + fieldWithPath("endDate").type(JsonFieldType.STRING).description("행사(Event) 종료일시"), + fieldWithPath("location").type(JsonFieldType.STRING).description("행사(Event) 장소"), + fieldWithPath("status").type(JsonFieldType.STRING).description("행사(Event) 진행 상태"), + fieldWithPath("tags[]").type(JsonFieldType.ARRAY).description("행사(Event) 연관 태그 목록") + ); + + //when + final ResultActions result = mockMvc.perform(delete("/events/" + eventId)); + + //then + result.andExpect(status().isNoContent()) + .andDo(print()) + .andDo(document("delete-event", responseFields)); + } + @Nested class AddEvent { diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index 11b263e8d..f71fff685 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -15,6 +15,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import com.emmsale.event.application.dto.EventDetailRequest; @@ -513,5 +514,37 @@ void updateEventWithNotExistTagTest() { assertEquals(exception.exceptionType(), EventExceptionType.NOT_FOUND_TAG); } } + + @Nested + class DeleteEvent { + + @Test + @DisplayName("이벤트를 성공적으로 삭제한다.") + void deleteEventTest() { + //given + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when + eventService.deleteEvent(eventId); + + //then + assertFalse(eventRepository.findById(eventId).isPresent()); + } + + @Test + @DisplayName("삭제할 이벤트가 존재하지 않을 경우 EventException이 발생한다.") + void deleteEventWithNotExistsEventTest() { + //given + final long notExistsEventId = 0L; + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventService.deleteEvent(notExistsEventId)); + + assertEquals(exception.exceptionType(), EventExceptionType.NOT_FOUND_EVENT); + } + } + } } From ccf846ef98ef6114c6e82e061eca9669cb54d295 Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 14:50:04 +0900 Subject: [PATCH 11/15] =?UTF-8?q?test:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=EC=8B=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=EA=B0=80=20=EC=A1=B4=EC=9E=AC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../event/application/EventServiceTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index f71fff685..0958618ca 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -460,6 +460,32 @@ void updateEventTest() { ); } + @Test + @DisplayName("업데이트할 이벤트가 존재하지 않을 경우 EventException이 발생한다.") + void updateEventWithNotExistsEventTest() { + //given + final long notExistsEventId = 0L; + + final String newName = "새로운 이름"; + final String newLocation = "새로운 장소"; + final LocalDateTime newStartDateTime = beforeDateTime; + final LocalDateTime newEndDateTime = afterDateTime; + final String newInformationUrl = "https://새로운-상세-URL.com"; + final List newTagRequests = List.of( + new TagRequest(IOS().getName()), + new TagRequest(AI().getName()) + ); + + final EventDetailRequest updateRequest = new EventDetailRequest(newName, newLocation, + newInformationUrl, newStartDateTime, newEndDateTime, newTagRequests); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventService.updateEvent(notExistsEventId, updateRequest)); + + assertEquals(exception.exceptionType(), EventExceptionType.NOT_FOUND_EVENT); + } + @Test @DisplayName("행사 시작 일시가 행사 종료 일시 이후일 경우 EventException이 발생한다.") void updateEventWithStartDateTimeAfterBeforeDateTimeTest() { From f4edde6a0a55bf0523c5bcbc52fef019bbf347d2 Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 14:51:37 +0900 Subject: [PATCH 12/15] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../com/emmsale/event/api/EventApiTest.java | 14 ++-- .../com/emmsale/event/domain/EventTest.java | 68 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java index 7addadef2..0dba3f712 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/api/EventApiTest.java @@ -209,7 +209,7 @@ void updateEventTest() throws Exception { final long eventId = 1L; final Event event = EventFixture.인프콘_2023(); - final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); @@ -308,7 +308,7 @@ void addEventTest() throws Exception { //given final Event event = EventFixture.인프콘_2023(); - final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); @@ -370,7 +370,7 @@ void addEventWithEmptyNameTest(final String eventName) throws Exception { //given final Event event = EventFixture.인프콘_2023(); - final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); @@ -400,7 +400,7 @@ void addEventWithEmptyLocationTest(final String eventLocation) throws Exception //given final Event event = EventFixture.인프콘_2023(); - final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); @@ -431,7 +431,7 @@ void addEventWithInvalidInformationUrlTest(final String informationUrl) throws E //given final Event event = EventFixture.인프콘_2023(); - final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); @@ -462,7 +462,7 @@ void addEventWithUnformattedStartDateTimeTest(final String startDateTime) throws //given final Event event = EventFixture.인프콘_2023(); - final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); @@ -493,7 +493,7 @@ void addEventWithUnformattedEndDateTimeTest(final String endDateTime) throws Exc //given final Event event = EventFixture.인프콘_2023(); - final List tags = List.of(TagFixture.백엔드(), TagFixture.안드로이드()).stream() + final List tags = Stream.of(TagFixture.백엔드(), TagFixture.안드로이드()) .map(tag -> new TagRequest(tag.getName())) .collect(Collectors.toList()); diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java index 242792325..b52464359 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java @@ -20,6 +20,40 @@ class EventTest { + @Test + @DisplayName("Event의 name, location, startDate, endDate, informationUrl, tags를 업데이트할 수 있다.") + void updateEventContentTest() { + //given + final String newName = "새로운 이름"; + final String newLocation = "새로운 장소"; + final LocalDateTime newStartDateTime = LocalDateTime.now(); + final LocalDateTime newEndDateTime = newStartDateTime.plusDays(1); + final String newInformationUrl = "https://새로운-상세-URL.com"; + final List newTags = List.of(TagFixture.IOS(), TagFixture.AI()); + + final Event event = EventFixture.인프콘_2023(); + + //when + final Event updatedEvent = event.updateEventContent( + newName, + newLocation, + newStartDateTime, + newEndDateTime, + newInformationUrl, + newTags + ); + + //then + assertAll( + () -> assertEquals(newName, updatedEvent.getName()), + () -> assertEquals(newLocation, updatedEvent.getLocation()), + () -> assertEquals(newStartDateTime, updatedEvent.getStartDate()), + () -> assertEquals(newEndDateTime, updatedEvent.getEndDate()), + () -> assertEquals(newInformationUrl, updatedEvent.getInformationUrl()), + () -> assertEquals(newTags.size(), event.getTags().size()) + ); + } + @Nested class addParticipant { @@ -55,39 +89,5 @@ void fail_alreadyContains() { .isInstanceOf(EventException.class) .hasMessage(EventExceptionType.ALREADY_PARTICIPATED.errorMessage()); } - - @Test - @DisplayName("Event의 name, location, startDate, endDate, informationUrl, tags를 업데이트할 수 있다.") - void updateEventContentTest() { - //given - final String newName = "새로운 이름"; - final String newLocation = "새로운 장소"; - final LocalDateTime newStartDateTime = LocalDateTime.now(); - final LocalDateTime newEndDateTime = newStartDateTime.plusDays(1); - final String newInformationUrl = "https://새로운-상세-URL.com"; - final List newTags = List.of(TagFixture.IOS(), TagFixture.AI()); - - final Event event = EventFixture.인프콘_2023(); - - //when - final Event updatedEvent = event.updateEventContent( - newName, - newLocation, - newStartDateTime, - newEndDateTime, - newInformationUrl, - newTags - ); - - //then - assertAll( - () -> assertEquals(newName, updatedEvent.getName()), - () -> assertEquals(newLocation, updatedEvent.getLocation()), - () -> assertEquals(newStartDateTime, updatedEvent.getStartDate()), - () -> assertEquals(newEndDateTime, updatedEvent.getEndDate()), - () -> assertEquals(newInformationUrl, updatedEvent.getInformationUrl()), - () -> assertEquals(newTags.size(), event.getTags().size()) - ); - } } } From 0a2bdd82979a9069fef8bdbd0f3ff6abb20361bc Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Sat, 29 Jul 2023 15:03:32 +0900 Subject: [PATCH 13/15] =?UTF-8?q?test:=20Event=20=EC=8B=9C=EC=9E=91=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A2=85=EB=A3=8C=20=EC=9D=BC=EC=8B=9C=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../com/emmsale/event/domain/EventTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java index b52464359..8b4ebac22 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import com.emmsale.event.EventFixture; import com.emmsale.event.exception.EventException; @@ -20,6 +21,23 @@ class EventTest { + @Test + @DisplayName("Event 생성시 startDate가 endDate 이후일 경우 EventException이 발생한다.") + void newEventWithStartDateAfterEndDateTest() { + //given + final String name = "이름"; + final String location = "장소"; + final String url = "https://information-url.com"; + final LocalDateTime beforeDateTime = LocalDateTime.now(); + final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> new Event(name, location, afterDateTime, beforeDateTime, url)); + + assertEquals(EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); + } + @Test @DisplayName("Event의 name, location, startDate, endDate, informationUrl, tags를 업데이트할 수 있다.") void updateEventContentTest() { @@ -54,6 +72,27 @@ void updateEventContentTest() { ); } + @Test + @DisplayName("eventContent 업데이트시 startDate가 endDate 이후일 경우 EventException이 발생한다.") + void updateEventContentWithStartDateAfterEndDateTest() { + //given + final String newName = "새로운 이름"; + final String newLocation = "새로운 장소"; + final LocalDateTime beforeDateTime = LocalDateTime.now(); + final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); + final String newInformationUrl = "https://새로운-상세-URL.com"; + final List newTags = List.of(TagFixture.IOS(), TagFixture.AI()); + + final Event event = EventFixture.인프콘_2023(); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> event.updateEventContent(newName, newLocation, afterDateTime, beforeDateTime, + newInformationUrl, newTags)); + + assertEquals(EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); + } + @Nested class addParticipant { From 7ca159cd17b16c80bad052be1767652248ecba12 Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Mon, 31 Jul 2023 17:37:25 +0900 Subject: [PATCH 14/15] =?UTF-8?q?refactor:=20=ED=83=9C=EA=B7=B8=EB=93=A4?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../emmsale/event/application/EventService.java | 4 +--- .../java/com/emmsale/event/domain/Event.java | 17 ++++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index ed31a68f2..540b6ddce 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -166,9 +166,7 @@ public EventDetailResponse addEvent(final EventDetailRequest request) { final List tags = findAllPersistTagsOrElseThrow(request.getTags()); - for (final Tag tag : tags) { - event.addEventTag(tag); - } + event.addAllEventTags(tags); return EventDetailResponse.from(event); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java index 375e9b00a..15a701661 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java @@ -12,6 +12,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; @@ -70,10 +71,14 @@ public Participant addParticipant(final Member member) { return participant; } - public EventTag addEventTag(final Tag tag) { - final EventTag eventTag = new EventTag(this, tag); - tags.add(eventTag); - return eventTag; + public List addAllEventTags(final List tags) { + final List eventTags = tags.stream() + .map(tag -> new EventTag(this, tag)) + .collect(Collectors.toList()); + + this.tags.addAll(eventTags); + + return eventTags; } public void validateAlreadyParticipate(final Member member) { @@ -114,9 +119,7 @@ public Event updateEventContent( this.informationUrl = informationUrl; this.tags = new ArrayList<>(); - for (final Tag tag : tags) { - addEventTag(tag); - } + addAllEventTags(tags); return this; } From 180a12b2c695b8670a0f104f238eeff5c66948da Mon Sep 17 00:00:00 2001 From: Hyeonjae-K Date: Mon, 31 Jul 2023 17:56:59 +0900 Subject: [PATCH 15/15] =?UTF-8?q?test:=20=EB=82=B4=EB=B6=80=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EA=B2=80=EC=A6=9D=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B3=B4=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #115 --- .../event/application/EventServiceTest.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index 0958618ca..f64fb8e3d 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -39,6 +39,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; +import java.util.stream.Collectors; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -363,7 +364,13 @@ void addEventTest() { () -> assertEquals(eventInformationUrl, savedEvent.getInformationUrl()), () -> assertEquals(startDateTime, savedEvent.getStartDate()), () -> assertEquals(endDateTime, savedEvent.getEndDate()), - () -> assertEquals(tagRequests.size(), savedEvent.getTags().size()) + () -> assertThat(savedEvent.getTags()).extracting("tag", Tag.class) + .extracting("name", String.class) + .containsAll( + tagRequests.stream() + .map(TagRequest::getName) + .collect(Collectors.toList()) + ) ); } @@ -456,7 +463,13 @@ void updateEventTest() { () -> assertEquals(newStartDateTime, updatedEvent.getStartDate()), () -> assertEquals(newEndDateTime, updatedEvent.getEndDate()), () -> assertEquals(newInformationUrl, updatedEvent.getInformationUrl()), - () -> assertEquals(newTagRequests.size(), updatedEvent.getTags().size()) + () -> assertThat(updatedEvent.getTags()).extracting("tag", Tag.class) + .extracting("name", String.class) + .containsAll( + newTagRequests.stream() + .map(TagRequest::getName) + .collect(Collectors.toList()) + ) ); }