Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

메시지 전송 api 구현 #24

Merged
merged 4 commits into from
Aug 30, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/java/web/chat/backend/controller/RoomController.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import web.chat.backend.controller.request.MessageRequest;
import web.chat.backend.controller.request.RoomRequest;
import web.chat.backend.controller.response.MessageResponse;
import web.chat.backend.controller.response.RoomResponse;
Expand Down Expand Up @@ -52,6 +53,19 @@ public List<MessageResponse> getMessages(@PathVariable Long id) {
return messages.stream().collect(new MessageCollector());
}

@PostMapping("/{roomId}/messages")
@ResponseStatus(HttpStatus.CREATED)
public MessageResponse createMessage(@PathVariable Long roomId, @RequestBody @Valid MessageRequest messageRequest) {
Message message = new Message();
message.setContents(messageRequest.getContents());

Room room = roomService.getOrThrow(roomId);

Message savedMessage = messageService.createMessage(room, message);

return new MessageResponse(savedMessage);
}

@PostMapping("")
@ResponseStatus(HttpStatus.CREATED)
public RoomResponse createRoom(@RequestBody @Valid RoomRequest roomRequest) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
package web.chat.backend.controller.request;

import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class MessageRequest {

@Size(min = 1)
private String contents;
}
3 changes: 3 additions & 0 deletions src/main/java/web/chat/backend/entity/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
Expand All @@ -13,13 +14,15 @@
import javax.persistence.ManyToOne;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@EntityListeners(AuditingEntityListener.class)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 나중에 공통 프로퍼티는 BaseEntity 클래스로 빼는 방법도 고려해봐야겠다

public class Message {

@Id
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/web/chat/backend/service/MessageService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import lombok.RequiredArgsConstructor;
import web.chat.backend.entity.Message;
import web.chat.backend.entity.Room;
import web.chat.backend.exception.NotFoundException;
import web.chat.backend.repository.MessageRepository;

Expand All @@ -21,4 +22,10 @@ public List<Message> getMessagesBy(Long roomId) {

return messageRepository.findAllByRoomId(roomId);
}

public Message createMessage(Room room, Message message) {
message.setRoom(room);

return messageRepository.save(message);
}
}
6 changes: 6 additions & 0 deletions src/main/java/web/chat/backend/service/RoomService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import lombok.RequiredArgsConstructor;
import web.chat.backend.entity.Room;
import web.chat.backend.exception.NotFoundException;
import web.chat.backend.repository.RoomRepository;

@RequiredArgsConstructor
Expand All @@ -21,4 +22,9 @@ public Room createRoom(Room room) {
public List<Room> getRooms() {
return roomRepository.findAll();
}

public Room getOrThrow(Long roomId) {
return roomRepository.findById(roomId)
.orElseThrow(() -> new NotFoundException(String.format("Room [id:%d] not found", roomId)));
}
Comment on lines +26 to +29
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유효하지 않는 roomId 를 받았을 때 null 을 리턴하지 않고 NotFoundException 을 발생시켜 각 계층 간 null을 주고받는 상황을 방지

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exists() 사용하지 않고 Optional 으로 하니까 깔끔하네 굿굿 👍 👍

public List<Message> getMessagesBy(Long roomId) {
if (!messageRepository.existsByRoomId(roomId)) {
throw new NotFoundException(roomId);
}
return messageRepository.findAllByRoomId(roomId);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package web.chat.backend.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestConstructor;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import web.chat.backend.controller.request.MessageRequest;

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@RequiredArgsConstructor
@AutoConfigureMockMvc
@SpringBootTest
public class MessageCreationTest {

final MockMvc mockMvc;
final ObjectMapper objectMapper;

final RoomController roomController;

@Test
@Sql("/test-sql/messages.sql")
void createMessage() throws Exception {

// given
MessageRequest req = new MessageRequest();
req.setContents("hi hello");

final String body = objectMapper.writeValueAsString(req);

// when
ResultActions action = mockMvc.perform(
post("/api/rooms/{roomId}/messages", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(body));

// then
action.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.contents").value(req.getContents()));
}
Comment on lines +31 to +51
Copy link
Contributor Author

@so3500 so3500 Aug 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 메서드를 MessageCreationTest 에 추가한 이유

이 테스트를 RoomControllerIntegrationTest 에 추가할 때 RoomControllerIntegrationTest#shouldGetMessageList_inRoomIdWith1 가 영향 받음

하나의 클래스에서 처리할 수 없을지 고민 필요

response

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json;charset=UTF-8"]
     Content type = application/json
             Body = [{"id":1,"contents":"foo","createdAt":[2020,8,1,0,0],"messageType":"TEXT"},{"id":3,"contents":"hi hello","createdAt":[2020,8,30,1,5,8,114499000],"messageType":"TEXT"},{"id":4,"contents":"foo","createdAt":[2020,8,1,0,0],"messageType":"TEXT"}]
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

error log

JSON path "$.*"
Expected: a collection with size <1>
     but: collection size was <3>
java.lang.AssertionError: JSON path "$.*"
Expected: a collection with size <1>
     but: collection size was <3>
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
	at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:73)
	at org.springframework.test.web.servlet.result.JsonPathResultMatchers.lambda$value$0(JsonPathResultMatchers.java:87)
	at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196)
	at web.chat.backend.controller.RoomControllerIntegrationTest.shouldGetMessageList_inRoomIdWith1(RoomControllerIntegrationTest.java:58)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at 
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#25 에서 고민해보기

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.any;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
Expand All @@ -21,9 +22,10 @@
import org.springframework.web.bind.MethodArgumentNotValidException;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import web.chat.backend.controller.request.MessageRequest;
import web.chat.backend.controller.request.RoomRequest;
import web.chat.backend.entity.Message;
import web.chat.backend.entity.Room;
import web.chat.backend.service.MessageService;
import web.chat.backend.service.RoomService;
Expand Down Expand Up @@ -118,4 +120,56 @@ void createRoom_titleLengthExceed_20() throws Exception {
.andDo(print());

}

@Test
void createMessage() throws Exception {

// given
MessageRequest req = new MessageRequest();
req.setContents("hi hello");

final String body = objectMapper.writeValueAsString(req);

Message expected = new Message();
expected.setId(1L);
expected.setContents(req.getContents());

given(roomService.getOrThrow(anyLong())).willReturn(new Room());
given(messageService.createMessage(any(), any())).willReturn(expected);

// when
ResultActions action = mockMvc.perform(
post("/api/rooms/1/messages")
.contentType(MediaType.APPLICATION_JSON)
.content(body));

// then
action.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(expected.getId()))
.andExpect(jsonPath("$.contents").value(expected.getContents()));
}

@DisplayName("Message 의 contents 빈값 불가능")
@Test
void createMessage_contentsLengthLessThan_1() throws Exception {

// given
MessageRequest req = new MessageRequest();
req.setContents("");

final String body = objectMapper.writeValueAsString(req);

// when
ResultActions action = mockMvc.perform(
post("/api/rooms/1/messages")
.contentType(MediaType.APPLICATION_JSON)
.content(body));

// then
action.andDo(print())
.andExpect(status().is4xxClientError())
.andExpect(result -> assertTrue(result.getResolvedException() instanceof MethodArgumentNotValidException));

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package web.chat.backend.repository;

import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.test.context.TestConstructor;

import lombok.RequiredArgsConstructor;
import web.chat.backend.entity.Message;
import web.chat.backend.entity.Room;

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@RequiredArgsConstructor
@EnableJpaAuditing
@DataJpaTest
class MessageRepositoryTest {

final TestEntityManager entityManager;
final MessageRepository messageRepository;

@Test
void save() {

// given
Room room = new Room();
room.setTitle("room");

entityManager.persist(room);

Message message = new Message();
message.setContents("hi hello");
message.setRoom(room);

// when
Message savedMessage = messageRepository.save(message);

// then
assertThat(savedMessage.getId()).isNotNull();
assertThat(savedMessage.getContents()).isEqualTo(message.getContents());
assertThat(savedMessage.getCreatedAt()).isNotNull();
assertThat(savedMessage.getRoom()).isNotNull();
assertThat(savedMessage.getRoom().getTitle()).isEqualTo(room.getTitle());
}
}