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

Ping 통신을 위한 데이터 타입 변경 #773

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

import com.ddang.ddang.chat.application.MessageService;
import com.ddang.ddang.chat.application.dto.CreateMessageDto;
import com.ddang.ddang.chat.application.dto.ReadMessageDto;
import com.ddang.ddang.chat.application.event.MessageNotificationEvent;
import com.ddang.ddang.chat.application.event.UpdateReadMessageLogEvent;
import com.ddang.ddang.chat.domain.Message;
import com.ddang.ddang.chat.domain.WebSocketChatSessions;
import com.ddang.ddang.chat.handler.dto.ChatMessageDataDto;
import com.ddang.ddang.chat.handler.dto.ChatPingDataDto;
import com.ddang.ddang.chat.handler.dto.HandleMessageResponse;
import com.ddang.ddang.chat.handler.dto.MessageDto;
import com.ddang.ddang.chat.handler.dto.SendMessageStatus;
import com.ddang.ddang.chat.presentation.dto.request.CreateMessageRequest;
import com.ddang.ddang.chat.presentation.dto.request.ReadMessageRequest;
import com.ddang.ddang.websocket.handler.WebSocketHandleTextMessageProvider;
import com.ddang.ddang.websocket.handler.dto.ChattingType;
import com.ddang.ddang.websocket.handler.dto.SendMessageDto;
import com.ddang.ddang.websocket.handler.dto.SessionAttributeDto;
import com.ddang.ddang.websocket.handler.dto.TextMessageType;
Expand All @@ -30,6 +36,9 @@
@RequiredArgsConstructor
public class ChatWebSocketHandleTextMessageProvider implements WebSocketHandleTextMessageProvider {

private static final String CHATROOM_ID_KEY = "chatRoomId";
private static final String CHATTTING_TYPE_KEY = "type";

private final WebSocketChatSessions sessions;
private final ObjectMapper objectMapper;
private final MessageService messageService;
Expand All @@ -47,15 +56,18 @@ public List<SendMessageDto> handleCreateSendMessage(
final Map<String, String> data
) throws JsonProcessingException {
final SessionAttributeDto sessionAttribute = getSessionAttributes(session);
final ChatMessageDataDto messageData = objectMapper.convertValue(data, ChatMessageDataDto.class);
sessions.add(session, messageData.chatRoomId());
final long chatRoomId = getChatRoomId(data);
sessions.add(session, chatRoomId);

final Long writerId = sessionAttribute.userId();
final CreateMessageDto createMessageDto = createMessageDto(messageData, writerId);
final Message message = messageService.create(createMessageDto);
sendNotificationIfReceiverNotInSession(message, sessionAttribute);
final ChattingType type = ChattingType.findValue(data.get(CHATTTING_TYPE_KEY));
if (ChattingType.PING == type) {
return createPingResponse(sessionAttribute, data, session);
}
return createSendMessageResponse(data, sessionAttribute);
Comment on lines +63 to +66
Copy link
Member

Choose a reason for hiding this comment

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

선택

당장 진행하지 않아도 괜찮긴 한데, WebSocketHandler에서 provider를 통해 chat 혹은 bid로 이동하는 것과 같이, chat type에 따라 클래스를 분리해 핸들링하는 것에 대해 어떻게 생각하시나요?
현재 해당 클래스에서 많은 역할이 있는 것 같아 의견 여쭤봅니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

좋은 의견 감사합니다!
말씀해주신 것처럼 WebSocketHandler에서 너무 많은 역할을 하고 있어서 chat type에 따라 클래스를 분리해 핸들링하도록 수정했습니다.

추가로 WebSocketHandler에서 PingHandler와 MessageHandler를 호출하여 데이터를 넘겨줄 때
WebSocketSession이나 Map<String, String>과 같은 원시 타입이 아닌 dto로 포장한 값을 전달하도록 수정했습니다.
원시 타입을 넘겨주니 원시 타입 내부에 어떤 데이터를 포함하고 있는지 모르겠더라구요!

이 부분이 괜찮은지 제이미의 의견이 있다면 궁금합니다!

}

return createSendMessages(message, writerId, createMessageDto.chatRoomId());
private long getChatRoomId(final Map<String, String> data) {
return Long.parseLong(data.get(CHATROOM_ID_KEY));
}

private SessionAttributeDto getSessionAttributes(final WebSocketSession session) {
Expand All @@ -64,6 +76,32 @@ private SessionAttributeDto getSessionAttributes(final WebSocketSession session)
return objectMapper.convertValue(attributes, SessionAttributeDto.class);
}

private List<SendMessageDto> createPingResponse(final SessionAttributeDto sessionAttribute, final Map<String, String> data, final WebSocketSession userSession) throws JsonProcessingException {
final ChatPingDataDto pingData = objectMapper.convertValue(data, ChatPingDataDto.class);
final ReadMessageRequest readMessageRequest = new ReadMessageRequest(sessionAttribute.userId(), pingData.chatRoomId(), pingData.lastMessageId());
final List<ReadMessageDto> readMessageDtos = messageService.readAllByLastMessageId(readMessageRequest);
Comment on lines +79 to +82
Copy link
Member

Choose a reason for hiding this comment

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

필수

해당 클래스에 기존 컨벤션이었던 120자를 초과하는 라인들이 많이 보이네요!!
그리고 메서드 순서도 호출순으로 보이지 않습니다..!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

인텔리제이 설정이 초기화되어서 정렬이 잘 안 되고 있었네요...ㅠㅠ
감사합니다. 수정했습니다!


final List<MessageDto> messageDtos = convertToMessageDto(readMessageDtos, userSession);
final HandleMessageResponse handleMessageResponse = new HandleMessageResponse(SendMessageStatus.SUCCESS, messageDtos);
return List.of(new SendMessageDto(userSession, new TextMessage(objectMapper.writeValueAsString(handleMessageResponse))));
}

private List<MessageDto> convertToMessageDto(final List<ReadMessageDto> readMessageDtos, final WebSocketSession session) {
return readMessageDtos.stream()
.map(readMessageDto -> MessageDto.of(readMessageDto, isMyMessage(session, readMessageDto.writerId())))
.toList();
}

private List<SendMessageDto> createSendMessageResponse(final Map<String, String> data, final SessionAttributeDto sessionAttribute) throws JsonProcessingException {
final Long writerId = sessionAttribute.userId();
final ChatMessageDataDto messageData = objectMapper.convertValue(data, ChatMessageDataDto.class);
final CreateMessageDto createMessageDto = createMessageDto(messageData, writerId);
final Message message = messageService.create(createMessageDto);
sendNotificationIfReceiverNotInSession(message, sessionAttribute);

return createSendMessages(message, writerId, createMessageDto.chatRoomId());
}

private CreateMessageDto createMessageDto(final ChatMessageDataDto messageData, final Long userId) {
final CreateMessageRequest request = new CreateMessageRequest(
messageData.receiverId(),
Expand Down Expand Up @@ -95,7 +133,8 @@ private List<SendMessageDto> createSendMessages(

final List<SendMessageDto> sendMessageDtos = new ArrayList<>();
for (final WebSocketSession currentSession : groupSessions) {
final TextMessage textMessage = createTextMessage(message, writerId, currentSession);
final MessageDto messageDto = MessageDto.of(message, isMyMessage(currentSession, writerId));
final TextMessage textMessage = createTextMessage(messageDto);
sendMessageDtos.add(new SendMessageDto(currentSession, textMessage));
updateReadMessageLog(currentSession, chatRoomId, message);
}
Expand All @@ -104,14 +143,11 @@ private List<SendMessageDto> createSendMessages(
}

private TextMessage createTextMessage(
final Message message,
final Long writerId,
final WebSocketSession session
final MessageDto messageDto
) throws JsonProcessingException {
final boolean isMyMessage = isMyMessage(session, writerId);
final MessageDto messageDto = MessageDto.of(message, isMyMessage);
final HandleMessageResponse handleMessageResponse = new HandleMessageResponse(SendMessageStatus.SUCCESS, List.of(messageDto));

return new TextMessage(objectMapper.writeValueAsString(messageDto));
return new TextMessage(objectMapper.writeValueAsString(handleMessageResponse));
}

private boolean isMyMessage(final WebSocketSession session, final Long writerId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.ddang.ddang.chat.handler.dto;

public record ChatPingDataDto(Long chatRoomId, Long lastMessageId) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ddang.ddang.chat.handler.dto;

import java.util.List;

public record HandleMessageResponse(SendMessageStatus sendMessageStatus, List<MessageDto> messages) {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ddang.ddang.chat.handler.dto;

import com.ddang.ddang.chat.application.dto.ReadMessageDto;
import com.ddang.ddang.chat.domain.Message;
import com.fasterxml.jackson.annotation.JsonFormat;

Expand All @@ -24,4 +25,13 @@ public static MessageDto of(final Message message, final boolean isMyMessage) {
message.getContents()
);
}

public static MessageDto of(final ReadMessageDto readMessageDto, final boolean isMyMessage) {
return new MessageDto(
readMessageDto.id(),
readMessageDto.createdTime(),
isMyMessage,
readMessageDto.contents()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ddang.ddang.chat.handler.dto;

public enum SendMessageStatus {

SUCCESS,
DISCONNECTED,
FORBIDDEN,
;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.ddang.ddang.websocket.handler.dto;

import java.util.Arrays;

public enum ChattingType {

MESSAGE("message"),
PING("ping"),
;
Comment on lines +7 to +9
Copy link
Member

Choose a reason for hiding this comment

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

질문

대소문자를 무관하게 하는 로직으로 수정하는 과정에서 value는 매핑을 위해 있는 것으로 보여 해당 값들을 없애고 name과 비교하는 로직으로 수정하려고 합니다. 괜찮을까요?
괜찮으시다면 제 쪽에서 추후 수정하려고 하는데, 메리의 코드기에 의견을 구하고 싶어 질문드립니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

저는 좋습니다 👍🏻


private String value;

ChattingType(final String value) {
this.value = value;
}

public static ChattingType findValue(final String value) {
return Arrays.stream(ChattingType.values())
.filter(chattingType -> chattingType.value.equals(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("잘못된 채팅 타입입니다."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willDoNothing;
Expand Down Expand Up @@ -173,6 +174,19 @@ class ChatWebSocketHandleTextMessageProviderTest extends ChatWebSocketHandleText
assertThat(actual).hasSize(1);
}

@Test
void 잘못된_데이터_타입_전달시_예외가_발생한다() throws JsonProcessingException {
// given
given(writerSession.getAttributes()).willReturn(발신자_세션_attribute_정보);
willDoNothing().given(sessions).add(writerSession, 채팅방.getId());
willReturn(false).given(sessions).containsByUserId(채팅방.getId(), 수신자.getId());
willReturn(Set.of(writerSession)).given(sessions).getSessionsByChatRoomId(채팅방.getId());

// when
assertThatThrownBy(() -> provider.handleCreateSendMessage(writerSession, 잘못된_메시지_전송_데이터))
.isInstanceOf(IllegalArgumentException.class);
Copy link
Member

Choose a reason for hiding this comment

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

선택

메시지는 비교하지 않아도 괜찮을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

메시지 내부 필드를 비교해야 하는지에 대한 질문이 맞나요?!

}

@Test
void 세션을_삭제한다() {
// given
Expand Down
Copy link
Member

Choose a reason for hiding this comment

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

선택

해당 클래스를 한 번 정렬해 주면 좋을 것 같아요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

정렬 완료 했습니다!

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.ddang.ddang.chat.application.event.CreateReadMessageLogEvent;
import com.ddang.ddang.chat.domain.ChatRoom;
import com.ddang.ddang.chat.domain.repository.ChatRoomRepository;
import com.ddang.ddang.chat.domain.repository.ReadMessageLogRepository;
import com.ddang.ddang.image.domain.ProfileImage;
import com.ddang.ddang.user.domain.Reliability;
import com.ddang.ddang.user.domain.User;
Expand Down Expand Up @@ -47,6 +46,7 @@ public class ChatWebSocketHandleTextMessageProviderTestFixture {
protected Map<String, Object> 발신자_세션_attribute_정보;
protected Map<String, Object> 수신자_세션_attribute_정보;
protected Map<String, String> 메시지_전송_데이터;
protected Map<String, String> 잘못된_메시지_전송_데이터;

protected CreateReadMessageLogEvent 메시지_로그_생성_이벤트;

Expand Down Expand Up @@ -89,10 +89,14 @@ void setUpFixture() {
발신자_세션_attribute_정보 = new HashMap<>(Map.of("userId", 발신자.getId(), "baseUrl", "/images"));
수신자_세션_attribute_정보 = new HashMap<>(Map.of("userId", 수신자.getId(), "baseUrl", "/images"));
메시지_전송_데이터 = Map.of(
"type", "message",
"chatRoomId", String.valueOf(채팅방.getId()),
"receiverId", String.valueOf(수신자.getId()),
"contents", "메시지 내용"
);
잘못된_메시지_전송_데이터 = Map.of(
"type", "wrong message type"
);

메시지_로그_생성_이벤트 = new CreateReadMessageLogEvent(채팅방);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,42 +68,42 @@ public class NotificationEventListenerFixture {
@BeforeEach
void setUpFixture() {
final User 발신자_겸_판매자 = User.builder()
.name("발신자 겸 판매자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("12345")
.build();
.name("발신자 겸 판매자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("12345")
.build();
final User 수신자_겸_기존_입찰자 = User.builder()
.name("수신자 겸 기존 입찰자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("12347")
.build();
.name("수신자 겸 기존 입찰자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("12347")
.build();
final User 새로운_입찰자 = User.builder()
.name("새로운 입찰자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("13579")
.build();
.name("새로운 입찰자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("13579")
.build();
final User 질문자 = User.builder()
.name("질문자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("12038")
.build();
.name("질문자")
.profileImage(new ProfileImage("upload.png", "store.png"))
.reliability(new Reliability(4.7d))
.oauthId("12038")
.build();
userRepository.save(발신자_겸_판매자);
userRepository.save(수신자_겸_기존_입찰자);
userRepository.save(새로운_입찰자);
userRepository.save(질문자);

final Auction 경매 = Auction.builder()
.seller(발신자_겸_판매자)
.title("경매글")
.description("경매글 설명")
.bidUnit(new BidUnit(100))
.startPrice(new Price(100))
.closingTime(LocalDateTime.now().plusDays(3L))
.build();
.seller(발신자_겸_판매자)
.title("경매글")
.description("경매글 설명")
.bidUnit(new BidUnit(100))
.startPrice(new Price(100))
.closingTime(LocalDateTime.now().plusDays(3L))
.build();
auctionRepository.save(경매);

final AuctionImage 경매_이미지 = new AuctionImage("upload.jpg", "store.jpg");
Expand All @@ -121,6 +121,7 @@ void setUpFixture() {
"baseUrl", 이미지_절대_경로
));
메시지_전송_데이터 = Map.of(
"type", "message",
"chatRoomId", String.valueOf(채팅방.getId()),
"receiverId", String.valueOf(수신자_겸_기존_입찰자.getId()),
"contents", "메시지 내용"
Expand All @@ -129,11 +130,11 @@ void setUpFixture() {

final Message 저장된_메시지 = messageRepository.save(
Message.builder()
.chatRoom(채팅방)
.writer(발신자_겸_판매자)
.receiver(수신자_겸_기존_입찰자)
.contents("메시지 내용")
.build()
.chatRoom(채팅방)
.writer(발신자_겸_판매자)
.receiver(수신자_겸_기존_입찰자)
.contents("메시지 내용")
.build()
);
final AuctionAndImageDto auctionAndImageDto = new AuctionAndImageDto(경매, 경매_이미지);
final BidDto 입찰_DTO = new BidDto(수신자_겸_기존_입찰자.getId(), auctionAndImageDto, 이미지_절대_경로);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.ddang.ddang.websocket.handler.dto;

import org.junit.jupiter.api.Test;

import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class ChattingTypeTest {

@Test
void 타입에_해당하는_enum을_반환한다() {
// given
final Map<String, String> data = Map.of("type", "message");

// when
final ChattingType actual = ChattingType.findValue(data.get("type"));

// then
assertThat(actual).isEqualTo(ChattingType.MESSAGE);
}


@Test
void 해당하는_타입이_없는_경우_예외를_던진다() {
// given
final Map<String, String> data = Map.of("type", "wrong type");

// when & then
assertThatThrownBy(() -> ChattingType.findValue(data.get("type"))).isInstanceOf(IllegalArgumentException.class);
}
}
Loading