From 913ae3061f37fd08dd7f3342d4f3af186d12f5e8 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Tue, 16 Jul 2024 12:51:51 +0900 Subject: [PATCH 01/23] =?UTF-8?q?Chore:=20Gradle=20=EB=A1=9C=EA=B9=85=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EB=A7=8C=20=ED=95=98=EA=B2=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index 23488ef..2b46879 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,12 @@ plugins { id 'checkstyle' } +configurations { + all { + exclude group: 'commons-logging', module: 'commons-logging' + } +} + editorconfig { excludes = ['build'] } From c068fb9538644e04bdbb43edafedb980c89d04a3 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 20 Jul 2024 01:43:54 +0900 Subject: [PATCH 02/23] =?UTF-8?q?Feat:=20=EB=AF=B8=ED=8C=85=EB=A3=B8=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MeetRoomController.java | 29 ++----- .../controller/MeetRoomControllerTest.java | 79 +++++++++++++++++++ 2 files changed, 86 insertions(+), 22 deletions(-) create mode 100644 src/test/java/capstone/relation/meeting/controller/MeetRoomControllerTest.java diff --git a/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java b/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java index fc1d7c3..42adc67 100644 --- a/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java +++ b/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java @@ -1,20 +1,17 @@ package capstone.relation.meeting.controller; -import org.springframework.http.HttpStatus; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ResponseStatusException; +import capstone.relation.common.util.SecurityUtil; import capstone.relation.meeting.dto.request.CreateRoomDto; import capstone.relation.meeting.dto.response.JoinResponseDto; import capstone.relation.meeting.dto.response.MeetingRoomListDto; import capstone.relation.meeting.service.MeetRoomService; -import capstone.relation.user.UserService; -import capstone.relation.user.domain.User; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -25,31 +22,21 @@ @RequiredArgsConstructor public class MeetRoomController { private final MeetRoomService meetRoomService; - private final UserService userService; @PostMapping("/create") @Operation(summary = "회의방 생성", description = "새로운 회의방을 생성합니다. 생성된 회의방은 `/topic/{workSpaceId}/meetingRoomList`로 생성된 방에 대한 목록 발송이 이루어집니다.\n" + "방 생성자는 자동으로 방에 참여합니다.\n" ) - public JoinResponseDto createRoom(@RequestBody CreateRoomDto createRoomDto) throws Exception { - User user = userService.getUserEntity(); - try { - return meetRoomService.createAndJoinRoom(createRoomDto, user.getId(), user.getWorkSpace().getId()); - } catch (Exception e) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); - } + public JoinResponseDto createRoom(@RequestBody CreateRoomDto createRoomDto) { + return meetRoomService.createAndJoinRoom(createRoomDto); } @PostMapping("/join/{roomId}") @Operation(summary = "회의방 참여", description = "회의방에 참여합니다.") public JoinResponseDto joinRoom(@PathVariable Long roomId) { - User user = userService.getUserEntity(); - try { - return meetRoomService.joinRoom(user.getId(), user.getWorkSpace().getId(), roomId); - } catch (Exception e) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); - } + //TODO:에러 발생 시켜보기. + return meetRoomService.joinRoom(roomId); } @GetMapping("/list") @@ -57,14 +44,12 @@ public JoinResponseDto joinRoom(@PathVariable Long roomId) { + "이것에 대한 응답은 `/topic/{workSpaceId}/meetingRoomList`로 이루어집니다.\n" ) public MeetingRoomListDto requestRoomList() { - User user = userService.getUserEntity(); - return meetRoomService.sendRoomList(user.getWorkSpace().getId()); + return meetRoomService.sendRoomList(); } @PostMapping("/leave") @Operation(summary = "회의방 나가기", description = "현재 참여중인 회의방을 나갑니다.") public void leaveRoom() { - User user = userService.getUserEntity(); - meetRoomService.leaveRoom(user.getId(), user.getWorkSpace().getId()); + meetRoomService.leaveRoom(SecurityUtil.getCurrentUserId()); } } diff --git a/src/test/java/capstone/relation/meeting/controller/MeetRoomControllerTest.java b/src/test/java/capstone/relation/meeting/controller/MeetRoomControllerTest.java new file mode 100644 index 0000000..fc12bf7 --- /dev/null +++ b/src/test/java/capstone/relation/meeting/controller/MeetRoomControllerTest.java @@ -0,0 +1,79 @@ +package capstone.relation.meeting.controller; + +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import capstone.relation.meeting.dto.request.CreateRoomDto; +import capstone.relation.meeting.dto.response.JoinResponseDto; +import capstone.relation.meeting.service.MeetRoomService; +import capstone.relation.security.WithMockCustomUser; +import capstone.relation.user.dto.UserInfoDto; + +@ExtendWith(SpringExtension.class) +@DisplayName("회의방 컨트롤러 테스트") +class MeetRoomControllerTest { + + @InjectMocks + private MeetRoomController meetRoomController; + + @Mock + private MeetRoomService meetRoomService; + + private MockMvc mockMvc; + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.standaloneSetup(meetRoomController).build(); + objectMapper = new ObjectMapper(); + } + + @Test + @DisplayName("방 생성") + void testCreateRoom() throws Exception { + CreateRoomDto createRoomDto = new CreateRoomDto("회의방"); + List userInfoList = new ArrayList<>(); + UserInfoDto userInfoDto = new UserInfoDto(1L, "이름", "이메일", "사진"); + userInfoList.add(userInfoDto); + JoinResponseDto joinResponseDto = new JoinResponseDto(1L, "회의방", userInfoList, 1L); + + when(meetRoomService.createAndJoinRoom(any(CreateRoomDto.class))) + .thenReturn(joinResponseDto); + + mockMvc.perform(post("/meet/room/create") + .contentType("application/json") + .content(objectMapper.writeValueAsString(createRoomDto))) // CreateRoomDto 객체를 JSON 문자열로 변환 + .andExpect(status().isOk()) + .andExpect(jsonPath("$.roomId").value(joinResponseDto.getRoomId())) + .andExpect(jsonPath("$.roomName").value(joinResponseDto.getRoomName())) + .andExpect(jsonPath("$.userInfoList[0].userId").value(userInfoDto.getUserId())) + .andExpect(jsonPath("$.userInfoList[0].userName").value(userInfoDto.getUserName())) + .andExpect(jsonPath("$.userInfoList[0].email").value(userInfoDto.getEmail())) + .andExpect(jsonPath("$.userInfoList[0].userImage").value(userInfoDto.getUserImage())) + .andExpect(jsonPath("$.userCount").value(joinResponseDto.getUserCount())); + } + + @Test + @DisplayName("방 나가기 테스트") + @WithMockCustomUser + void testLeaveRoom() throws Exception { + mockMvc.perform(post("/meet/room/leave")) + .andExpect(status().isOk()); + } +} From 4ae46c462fb81148a316dd90419f6a2f68b1376a Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 20 Jul 2024 13:32:59 +0900 Subject: [PATCH 03/23] =?UTF-8?q?Refactor:=20MeetRoomService=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9C=A0=EC=A0=80=20=EB=A1=9C=EC=A7=81=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../relation/api/auth/jwt/SecurityUser.java | 2 +- .../meeting/service/MeetRoomService.java | 53 ++++++++----------- .../capstone/relation/user/UserService.java | 20 +++++++ .../capstone/relation/user/domain/User.java | 1 + .../relation/user/dto/UserInfoDto.java | 15 +++++- .../websocket/WebSocketEventListener.java | 2 +- 6 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/main/java/capstone/relation/api/auth/jwt/SecurityUser.java b/src/main/java/capstone/relation/api/auth/jwt/SecurityUser.java index 022c337..84be3de 100644 --- a/src/main/java/capstone/relation/api/auth/jwt/SecurityUser.java +++ b/src/main/java/capstone/relation/api/auth/jwt/SecurityUser.java @@ -72,7 +72,7 @@ public Long getUserId() { return userId; } - private SecurityUser(Long userId, Role role) { + public SecurityUser(Long userId, Role role) { this.userId = userId; this.role = role; } diff --git a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java index 6dcff03..352b943 100644 --- a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java +++ b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java @@ -17,15 +17,16 @@ import org.springframework.web.server.ResponseStatusException; import capstone.relation.api.auth.exception.AuthException; +import capstone.relation.common.util.SecurityUtil; import capstone.relation.meeting.domain.MeetRoom; import capstone.relation.meeting.dto.request.CreateRoomDto; import capstone.relation.meeting.dto.response.JoinResponseDto; import capstone.relation.meeting.dto.response.MeetingRoomDto; import capstone.relation.meeting.dto.response.MeetingRoomListDto; import capstone.relation.meeting.repository.MeetRoomRepository; +import capstone.relation.user.UserService; import capstone.relation.user.dto.RoomInfoDto; import capstone.relation.user.dto.UserInfoDto; -import capstone.relation.user.repository.UserRepository; import capstone.relation.websocket.SocketRegistry; import capstone.relation.workspace.WorkSpace; import capstone.relation.workspace.repository.WorkSpaceRepository; @@ -38,7 +39,7 @@ public class MeetRoomService { private static final String WORK_KEY = "WORKSPACE_ROOM_PARTICIPANTS"; private static final String USER_KEY = "USER_ROOM_MAPPING"; - private final UserRepository userRepository; + private final UserService userService; private final WorkSpaceRepository workSpaceRepository; private final MeetRoomRepository meetRoomRepository; private final SocketRegistry socketRegistry; @@ -55,11 +56,13 @@ protected void init() { } @Transactional(readOnly = false) - public JoinResponseDto createAndJoinRoom(CreateRoomDto createRoomDto, Long userId, String workSpaceId) { + public JoinResponseDto createAndJoinRoom(CreateRoomDto createRoomDto) { + Long userId = SecurityUtil.getCurrentUserId(); String roomName = createRoomDto.getRoomName(); if (roomName == null || roomName.isEmpty()) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "회의실 이름을 입력해주세요."); } + String workSpaceId = userService.getUserWorkSpaceId(userId); try { JoinResponseDto joinResponseDto = createAndJoin(workSpaceId, userId.toString(), roomName); sendRoomList(workSpaceId); @@ -83,12 +86,7 @@ public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { System.out.println("roomId = " + roomId); System.out.println("userId = " + userId); Set userIds = getRoomMembers(workspaceId, Long.parseLong(roomId)); - List userInfoList = new ArrayList<>(); - for (String id : userIds) { - UserInfoDto userInfoDto = new UserInfoDto(); - userRepository.findById(Long.parseLong(id)).ifPresent(userInfoDto::setByUserEntity); - userInfoList.add(userInfoDto); - } + List userInfoList = userService.getUserInfoList(userIds); return new RoomInfoDto(true, Long.parseLong(roomId), meetRoomRepository.findById(Long.parseLong(roomId)).get().getRoomName(), userInfoList); } @@ -110,13 +108,14 @@ private JoinResponseDto createAndJoin(String workSpaceId, String userId, String return joinWorkspaceRoom(workSpaceId, userId, roomId); } - public JoinResponseDto joinRoom(Long userId, String workspaceId, Long roomId) { - + public JoinResponseDto joinRoom(Long roomId) { + Long userId = SecurityUtil.getCurrentUserId(); //유저가 이미 회의 방에 있는지 확인 + String workspaceId = userService.getUserWorkSpaceId(userId); String meetRoom = userRoomMapping.get(USER_KEY, userId.toString()); - if (meetRoom != null) { + if (meetRoom != null) throw new IllegalArgumentException("User is already in the room: " + userId); - } + JoinResponseDto joinResponseDto = joinWorkspaceRoom(workspaceId, userId.toString(), roomId); sendRoomList(workspaceId); return joinResponseDto; @@ -126,18 +125,14 @@ private JoinResponseDto joinWorkspaceRoom(String workSpaceId, String userId, Lon MeetRoom meetRoom = meetRoomRepository.findById(roomId) .orElseThrow(() -> new IllegalArgumentException("Invalid room ID")); Set userIds = addUserToRoom(workSpaceId, roomId, userId); - List userInfoList = new ArrayList<>(); - for (String id : userIds) { - UserInfoDto userInfoDto = new UserInfoDto(); - userRepository.findById(Long.parseLong(id)).ifPresent(userInfoDto::setByUserEntity); - userInfoList.add(userInfoDto); - } + List userInfoList = userService.getUserInfoList(userIds); sendUserList(workSpaceId, roomId); return new JoinResponseDto(roomId, meetRoom.getRoomName(), userInfoList, (long)userIds.size()); } @Transactional(readOnly = false) - public void leaveRoom(Long userId, String workspaceId) { + public void leaveRoom(Long userId) { + String workspaceId = userService.getUserWorkSpaceId(userId); String meetRoom = userRoomMapping.get(USER_KEY, userId.toString()); if (meetRoom == null) { return; @@ -211,11 +206,6 @@ public MeetingRoomListDto sendRoomList(String workSpaceId) { } meetingRoomDto.setUserCount(roomMembers.size()); List userInfoList = new ArrayList<>(); - for (String userId : roomMembers) { - UserInfoDto userInfoDto = new UserInfoDto(); - userRepository.findById(Long.parseLong(userId)).ifPresent(userInfoDto::setByUserEntity); - userInfoList.add(userInfoDto); - } meetingRoomDto.setUserInfoList(userInfoList); meetingRoomListDto.getMeetingRoomList().add(meetingRoomDto); } @@ -223,14 +213,15 @@ public MeetingRoomListDto sendRoomList(String workSpaceId) { return meetingRoomListDto; } + public MeetingRoomListDto sendRoomList() { + Long userId = SecurityUtil.getCurrentUserId(); + String workSpaceId = userService.getUserWorkSpaceId(userId); + return sendRoomList(workSpaceId); + } + private void sendUserList(String workSpaceId, Long roomId) { Set userIds = getRoomMembers(workSpaceId, roomId); - List userInfoList = new ArrayList<>(); - for (String userId : userIds) { - UserInfoDto userInfoDto = new UserInfoDto(); - userRepository.findById(Long.parseLong(userId)).ifPresent(userInfoDto::setByUserEntity); - userInfoList.add(userInfoDto); - } + List userInfoList = userService.getUserInfoList(userIds); simpMessagingTemplate.convertAndSend("/topic/meetingRoom/" + roomId + "/users", userInfoList); } diff --git a/src/main/java/capstone/relation/user/UserService.java b/src/main/java/capstone/relation/user/UserService.java index fe8ff03..653664a 100644 --- a/src/main/java/capstone/relation/user/UserService.java +++ b/src/main/java/capstone/relation/user/UserService.java @@ -1,6 +1,9 @@ package capstone.relation.user; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.Set; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; @@ -72,4 +75,21 @@ public void leaveWorkspace() { user.setWorkSpace(null); userRepository.save(user); } + + public String getUserWorkSpaceId(Long userId) { + return userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")) + .getWorkSpace() + .getId(); + } + + public List getUserInfoList(Set userIds) { + List userInfoList = new ArrayList<>(); + for (String id : userIds) { + UserInfoDto userInfoDto = new UserInfoDto(); + userRepository.findById(Long.parseLong(id)).ifPresent(userInfoDto::setByUserEntity); + userInfoList.add(userInfoDto); + } + return userInfoList; + } } diff --git a/src/main/java/capstone/relation/user/domain/User.java b/src/main/java/capstone/relation/user/domain/User.java index 32d1034..b9d837a 100644 --- a/src/main/java/capstone/relation/user/domain/User.java +++ b/src/main/java/capstone/relation/user/domain/User.java @@ -36,6 +36,7 @@ public User(Long id, String profileImage, String userName, String email, String this.provider = provider; this.email = email; this.role = role; + this.id = id; } @Id diff --git a/src/main/java/capstone/relation/user/dto/UserInfoDto.java b/src/main/java/capstone/relation/user/dto/UserInfoDto.java index f469b2e..6b0c184 100644 --- a/src/main/java/capstone/relation/user/dto/UserInfoDto.java +++ b/src/main/java/capstone/relation/user/dto/UserInfoDto.java @@ -3,9 +3,13 @@ import capstone.relation.user.domain.User; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; -@Data +@Getter +@Setter +@NoArgsConstructor public class UserInfoDto { @Schema(description = "사용자 ID", example = "1234567890") private Long userId; @@ -16,6 +20,13 @@ public class UserInfoDto { @Schema(description = "사용자 이메일", example = "wnddms12345@naver.com") private String email; + public UserInfoDto(Long userId, String userName, String email, String userImage) { + this.userId = userId; + this.userName = userName; + this.email = email; + this.userImage = userImage; + } + @Hidden public void setByUserEntity(User user) { this.userId = user.getId(); diff --git a/src/main/java/capstone/relation/websocket/WebSocketEventListener.java b/src/main/java/capstone/relation/websocket/WebSocketEventListener.java index 9c70a5a..1c54f00 100644 --- a/src/main/java/capstone/relation/websocket/WebSocketEventListener.java +++ b/src/main/java/capstone/relation/websocket/WebSocketEventListener.java @@ -27,7 +27,7 @@ public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { if (userId == null) { return; } - meetRoomService.leaveRoom(userId, workSpaceId); + meetRoomService.leaveRoom(userId); System.out.println("register SocketId" + socketRegistry.getSocketId(userId.toString())); if (socketId == socketRegistry.getSocketId(userId.toString())) { socketRegistry.unregisterSession(userId.toString()); From 8d0d13096947523536a84f157a66e80b8313bcee Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 20 Jul 2024 13:52:43 +0900 Subject: [PATCH 04/23] =?UTF-8?q?Refactor:=20MeetRoomService=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20RedisRepository=20=ED=8C=A8=ED=84=B4=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/repository/RedisRepository.java | 78 +++++++++++++ .../meeting/service/MeetRoomService.java | 104 +++--------------- 2 files changed, 95 insertions(+), 87 deletions(-) create mode 100644 src/main/java/capstone/relation/meeting/repository/RedisRepository.java diff --git a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java new file mode 100644 index 0000000..d05b255 --- /dev/null +++ b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java @@ -0,0 +1,78 @@ +package capstone.relation.meeting.repository; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Repository; + +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class RedisRepository { + private static final String WORK_KEY = "WORKSPACE_ROOM_PARTICIPANTS"; + private static final String USER_KEY = "USER_ROOM_MAPPING"; + private final RedisTemplate redisTemplate; + private HashOperations>> workspaceRoomParticipants; + private HashOperations userRoomMapping; + + @PostConstruct + protected void init() { + workspaceRoomParticipants = redisTemplate.opsForHash(); + userRoomMapping = redisTemplate.opsForHash(); + } + + public boolean isUserInRoom(String userId) { + return userRoomMapping.get(USER_KEY, userId) != null; + } + + public String getUserRoom(String userId) { + return userRoomMapping.get(USER_KEY, userId); + } + + public void addUserToRoom(String workspaceId, Long roomId, String userId) { + HashMap> roomParticipants = workspaceRoomParticipants.get(WORK_KEY, workspaceId); + if (roomParticipants == null) + roomParticipants = new HashMap<>(); + Set users = roomParticipants.get(roomId.toString()); + if (users == null) + users = new HashSet<>(); + + users.add(userId); + roomParticipants.put(roomId.toString(), users); + workspaceRoomParticipants.put(WORK_KEY, workspaceId, roomParticipants); + userRoomMapping.put(USER_KEY, userId, roomId.toString()); + } + + public void removeUserFromRoom(String workspaceId, Long roomId, String userId) { + userRoomMapping.delete(USER_KEY, userId); + HashMap> roomParticipants = workspaceRoomParticipants.get(WORK_KEY, workspaceId); + if (roomParticipants == null) + return; + Set userIds = roomParticipants.get(roomId.toString()); + userIds.remove(userId); + + if (userIds.isEmpty()) + roomParticipants.remove(roomId.toString()); + else + roomParticipants.put(roomId.toString(), userIds); + + if (roomParticipants.isEmpty()) + workspaceRoomParticipants.delete(WORK_KEY, workspaceId); + else + workspaceRoomParticipants.put(WORK_KEY, workspaceId, roomParticipants); + + } + + public Set getRoomMembers(String workspaceId, Long roomId) { + HashMap> roomParticipants = workspaceRoomParticipants.get(WORK_KEY, workspaceId); + if (roomParticipants == null) { + return new HashSet<>(); + } + return roomParticipants.get(roomId.toString()); + } +} \ No newline at end of file diff --git a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java index 352b943..88363b2 100644 --- a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java +++ b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java @@ -1,13 +1,9 @@ package capstone.relation.meeting.service; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Set; -import org.springframework.data.redis.core.HashOperations; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; @@ -24,44 +20,32 @@ import capstone.relation.meeting.dto.response.MeetingRoomDto; import capstone.relation.meeting.dto.response.MeetingRoomListDto; import capstone.relation.meeting.repository.MeetRoomRepository; +import capstone.relation.meeting.repository.RedisRepository; import capstone.relation.user.UserService; import capstone.relation.user.dto.RoomInfoDto; import capstone.relation.user.dto.UserInfoDto; import capstone.relation.websocket.SocketRegistry; import capstone.relation.workspace.WorkSpace; import capstone.relation.workspace.repository.WorkSpaceRepository; -import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class MeetRoomService { - private static final String WORK_KEY = "WORKSPACE_ROOM_PARTICIPANTS"; - private static final String USER_KEY = "USER_ROOM_MAPPING"; private final UserService userService; private final WorkSpaceRepository workSpaceRepository; private final MeetRoomRepository meetRoomRepository; + private final RedisRepository redisRepository; private final SocketRegistry socketRegistry; - private final RedisTemplate redisTemplate; private final SimpMessagingTemplate simpMessagingTemplate; - private HashOperations>> workspaceRoomParticipants; - //workspaceId, roomId, userIds - private HashOperations userRoomMapping; - - @PostConstruct - protected void init() { - workspaceRoomParticipants = redisTemplate.opsForHash(); - userRoomMapping = redisTemplate.opsForHash(); - } @Transactional(readOnly = false) public JoinResponseDto createAndJoinRoom(CreateRoomDto createRoomDto) { Long userId = SecurityUtil.getCurrentUserId(); String roomName = createRoomDto.getRoomName(); - if (roomName == null || roomName.isEmpty()) { + if (roomName == null || roomName.isEmpty()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "회의실 이름을 입력해주세요."); - } String workSpaceId = userService.getUserWorkSpaceId(userId); try { JoinResponseDto joinResponseDto = createAndJoin(workSpaceId, userId.toString(), roomName); @@ -75,17 +59,15 @@ public JoinResponseDto createAndJoinRoom(CreateRoomDto createRoomDto) { } public boolean isUserInRoom(String userId) { - return userRoomMapping.get(USER_KEY, userId) != null; + return redisRepository.isUserInRoom(userId); } public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { - if (!isUserInRoom(userId.toString())) { + if (!isUserInRoom(userId.toString())) return new RoomInfoDto(); - } - String roomId = userRoomMapping.get(USER_KEY, userId.toString()); - System.out.println("roomId = " + roomId); - System.out.println("userId = " + userId); - Set userIds = getRoomMembers(workspaceId, Long.parseLong(roomId)); + + String roomId = redisRepository.getUserRoom(userId.toString()); + Set userIds = redisRepository.getRoomMembers(workspaceId, Long.parseLong(roomId)); List userInfoList = userService.getUserInfoList(userIds); return new RoomInfoDto(true, Long.parseLong(roomId), meetRoomRepository.findById(Long.parseLong(roomId)).get().getRoomName(), userInfoList); @@ -94,9 +76,8 @@ public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { private JoinResponseDto createAndJoin(String workSpaceId, String userId, String roomName) { WorkSpace workSpace = workSpaceRepository.findById(workSpaceId) .orElseThrow(() -> new IllegalArgumentException("Invalid workspace ID")); - if (isUserInRoom(userId)) { + if (isUserInRoom(userId)) throw new IllegalArgumentException("User is already in the room: " + userId); - } MeetRoom meetRoom = MeetRoom.builder() .roomName(roomName) @@ -110,9 +91,8 @@ private JoinResponseDto createAndJoin(String workSpaceId, String userId, String public JoinResponseDto joinRoom(Long roomId) { Long userId = SecurityUtil.getCurrentUserId(); - //유저가 이미 회의 방에 있는지 확인 String workspaceId = userService.getUserWorkSpaceId(userId); - String meetRoom = userRoomMapping.get(USER_KEY, userId.toString()); + String meetRoom = redisRepository.getUserRoom(userId.toString()); if (meetRoom != null) throw new IllegalArgumentException("User is already in the room: " + userId); @@ -124,7 +104,8 @@ public JoinResponseDto joinRoom(Long roomId) { private JoinResponseDto joinWorkspaceRoom(String workSpaceId, String userId, Long roomId) { MeetRoom meetRoom = meetRoomRepository.findById(roomId) .orElseThrow(() -> new IllegalArgumentException("Invalid room ID")); - Set userIds = addUserToRoom(workSpaceId, roomId, userId); + redisRepository.addUserToRoom(workSpaceId, roomId, userId); + Set userIds = redisRepository.getRoomMembers(workSpaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); sendUserList(workSpaceId, roomId); return new JoinResponseDto(roomId, meetRoom.getRoomName(), userInfoList, (long)userIds.size()); @@ -133,65 +114,14 @@ private JoinResponseDto joinWorkspaceRoom(String workSpaceId, String userId, Lon @Transactional(readOnly = false) public void leaveRoom(Long userId) { String workspaceId = userService.getUserWorkSpaceId(userId); - String meetRoom = userRoomMapping.get(USER_KEY, userId.toString()); - if (meetRoom == null) { + String meetRoom = redisRepository.getUserRoom(userId.toString()); + if (meetRoom == null) return; - } - removeUserFromRoom(workspaceId, Long.parseLong(meetRoom), userId.toString()); + redisRepository.removeUserFromRoom(workspaceId, Long.parseLong(meetRoom), userId.toString()); sendUserList(workspaceId, Long.parseLong(meetRoom)); sendRoomList(workspaceId); } - private Set addUserToRoom(String workspaceId, Long roomId, String userId) { - HashMap> roomParticipants = workspaceRoomParticipants.get(WORK_KEY, workspaceId); - if (roomParticipants == null) { - roomParticipants = new HashMap<>(); - } - Set users = roomParticipants.get(roomId.toString()); - if (users == null) { - users = new HashSet<>(); - } - users.add(userId); - roomParticipants.put(roomId.toString(), users); - workspaceRoomParticipants.put(WORK_KEY, workspaceId, roomParticipants); - userRoomMapping.put(USER_KEY, userId, roomId.toString()); - return users; - } - - private void removeUserFromRoom(String workspaceId, Long roomId, String userId) { - userRoomMapping.delete(USER_KEY, userId); - HashMap> roomParticipants = workspaceRoomParticipants.get(WORK_KEY, workspaceId); - if (roomParticipants == null) { - return; - } - Set userIds = roomParticipants.get(roomId.toString()); - userIds.remove(userId); - - if (userIds.isEmpty()) { - MeetRoom meetRoom = meetRoomRepository.findById(roomId).orElse(null); - if (meetRoom != null) { - meetRoom.setDeleted(true); - meetRoomRepository.save(meetRoom); - } - roomParticipants.remove(roomId.toString()); - } else { - roomParticipants.put(roomId.toString(), userIds); - } - if (roomParticipants.isEmpty()) { - workspaceRoomParticipants.delete(WORK_KEY, workspaceId); - } else { - workspaceRoomParticipants.put(WORK_KEY, workspaceId, roomParticipants); - } - } - - public Set getRoomMembers(String workspaceId, Long roomId) { - HashMap> roomParticipants = workspaceRoomParticipants.get(WORK_KEY, workspaceId.toString()); - if (roomParticipants == null) { - return new HashSet<>(); - } - return roomParticipants.get(roomId.toString()); - } - public MeetingRoomListDto sendRoomList(String workSpaceId) { Set meetRooms = meetRoomRepository.findAllByWorkSpaceId(workSpaceId); MeetingRoomListDto meetingRoomListDto = new MeetingRoomListDto(); @@ -200,7 +130,7 @@ public MeetingRoomListDto sendRoomList(String workSpaceId) { MeetingRoomDto meetingRoomDto = new MeetingRoomDto(); meetingRoomDto.setRoomId(roomId); meetingRoomDto.setRoomName(meetRoom.getRoomName()); - Set roomMembers = getRoomMembers(workSpaceId, roomId); + Set roomMembers = redisRepository.getRoomMembers(workSpaceId, roomId); if (roomMembers == null) { return meetingRoomListDto; } @@ -220,7 +150,7 @@ public MeetingRoomListDto sendRoomList() { } private void sendUserList(String workSpaceId, Long roomId) { - Set userIds = getRoomMembers(workSpaceId, roomId); + Set userIds = redisRepository.getRoomMembers(workSpaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); simpMessagingTemplate.convertAndSend("/topic/meetingRoom/" + roomId + "/users", userInfoList); } From a133ed5238be93269f1785ed52dda834d646ce68 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 20 Jul 2024 14:03:05 +0900 Subject: [PATCH 05/23] chore: checkStyle Rule change --- config/naver-checkstyle-rules.xml | 10 +++++----- .../relation/meeting/repository/RedisRepository.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config/naver-checkstyle-rules.xml b/config/naver-checkstyle-rules.xml index 1d93b2f..06d3541 100644 --- a/config/naver-checkstyle-rules.xml +++ b/config/naver-checkstyle-rules.xml @@ -221,11 +221,11 @@ The following rules in the Naver coding convention cannot be checked by this con value="[sub-flow-after-brace] ''{0}'' at column {1} should have line break before."/> - - - - + + + + + diff --git a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java index d05b255..a5ba545 100644 --- a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java +++ b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java @@ -60,7 +60,7 @@ public void removeUserFromRoom(String workspaceId, Long roomId, String userId) { roomParticipants.remove(roomId.toString()); else roomParticipants.put(roomId.toString(), userIds); - + if (roomParticipants.isEmpty()) workspaceRoomParticipants.delete(WORK_KEY, workspaceId); else @@ -75,4 +75,4 @@ public Set getRoomMembers(String workspaceId, Long roomId) { } return roomParticipants.get(roomId.toString()); } -} \ No newline at end of file +} From 44ada4a9f0ff24904e9ffda6b34b7afcd1b7f607 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 20 Jul 2024 14:03:29 +0900 Subject: [PATCH 06/23] =?UTF-8?q?Feat:=20=EC=9C=A0=EC=A0=80=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20util=20class=20=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../relation/common/util/SecurityUtil.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/capstone/relation/common/util/SecurityUtil.java diff --git a/src/main/java/capstone/relation/common/util/SecurityUtil.java b/src/main/java/capstone/relation/common/util/SecurityUtil.java new file mode 100644 index 0000000..3ff8392 --- /dev/null +++ b/src/main/java/capstone/relation/common/util/SecurityUtil.java @@ -0,0 +1,30 @@ +package capstone.relation.common.util; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +import capstone.relation.api.auth.jwt.SecurityUser; + +public class SecurityUtil { + + private SecurityUtil() { + throw new IllegalStateException("Utility class"); + } + + public static UserDetails getCurrentUserDetails() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null && authentication.getPrincipal() instanceof UserDetails) { + return (UserDetails)authentication.getPrincipal(); + } + throw new IllegalStateException("User not authenticated"); + } + + public static Long getCurrentUserId() { + UserDetails userDetails = getCurrentUserDetails(); + if (userDetails instanceof SecurityUser) { // Assuming SecurityUser extends UserDetails + return ((SecurityUser)userDetails).getUserId(); + } + throw new IllegalStateException("UserDetails does not contain userId"); + } +} From ebbea112e5ae663eeca629719a3e7a5b43e265ed Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 20 Jul 2024 14:07:18 +0900 Subject: [PATCH 07/23] Feat: Test Security context Set annotation --- .../relation/security/WithMockCustomUser.java | 14 +++++++++++ ...hMockCustomUserSecurityContextFactory.java | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/test/java/capstone/relation/security/WithMockCustomUser.java create mode 100644 src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java diff --git a/src/test/java/capstone/relation/security/WithMockCustomUser.java b/src/test/java/capstone/relation/security/WithMockCustomUser.java new file mode 100644 index 0000000..dd1b207 --- /dev/null +++ b/src/test/java/capstone/relation/security/WithMockCustomUser.java @@ -0,0 +1,14 @@ +package capstone.relation.security; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.springframework.security.test.context.support.WithSecurityContext; + +@Retention(RetentionPolicy.RUNTIME) +@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class) +public @interface WithMockCustomUser { + long id() default 1L; + + String role() default "USER"; +} \ No newline at end of file diff --git a/src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java b/src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java new file mode 100644 index 0000000..95be24d --- /dev/null +++ b/src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java @@ -0,0 +1,24 @@ +package capstone.relation.security; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithSecurityContextFactory; + +import capstone.relation.api.auth.jwt.SecurityUser; + +public class WithMockCustomUserSecurityContextFactory + implements WithSecurityContextFactory { + + @Override + public SecurityContext createSecurityContext(WithMockCustomUser customUser) { + System.out.println("customUser: " + customUser); + SecurityContext context = SecurityContextHolder.createEmptyContext(); + SecurityUser securityUser = SecurityUser.of(customUser.id(), customUser.role()); + System.out.println("securityUser: " + securityUser); + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(securityUser, null, + securityUser.getAuthorities()); + context.setAuthentication(auth); + return context; + } +} \ No newline at end of file From 463a50a3113fd3d3dd9ccc33eb6493c92b5ac363 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Mon, 22 Jul 2024 09:29:11 +0900 Subject: [PATCH 08/23] Feat: Test CustomInitEvent --- .../common/initializer/CustomInitEvent.java | 2 ++ .../initializer/DatabaseInitializer.java | 2 ++ .../relation/CustomInitListenerTest.java | 26 +++++++++++++++++++ .../relation/TestCustomInitConfig.java | 18 +++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 src/test/java/capstone/relation/CustomInitListenerTest.java create mode 100644 src/test/java/capstone/relation/TestCustomInitConfig.java diff --git a/src/main/java/capstone/relation/common/initializer/CustomInitEvent.java b/src/main/java/capstone/relation/common/initializer/CustomInitEvent.java index 3bbdaee..57cca16 100644 --- a/src/main/java/capstone/relation/common/initializer/CustomInitEvent.java +++ b/src/main/java/capstone/relation/common/initializer/CustomInitEvent.java @@ -1,7 +1,9 @@ package capstone.relation.common.initializer; import org.springframework.context.ApplicationEvent; +import org.springframework.context.annotation.Profile; +@Profile("dev") public class CustomInitEvent extends ApplicationEvent { // 여기에 추가 데이터 필드나 메서드를 정의할 수 있습니다. diff --git a/src/main/java/capstone/relation/common/initializer/DatabaseInitializer.java b/src/main/java/capstone/relation/common/initializer/DatabaseInitializer.java index 501226a..e21e504 100644 --- a/src/main/java/capstone/relation/common/initializer/DatabaseInitializer.java +++ b/src/main/java/capstone/relation/common/initializer/DatabaseInitializer.java @@ -2,6 +2,7 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Profile; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @@ -9,6 +10,7 @@ @Component @RequiredArgsConstructor +@Profile("dev") public class DatabaseInitializer implements ApplicationListener { private final ApplicationEventPublisher eventPublisher; diff --git a/src/test/java/capstone/relation/CustomInitListenerTest.java b/src/test/java/capstone/relation/CustomInitListenerTest.java new file mode 100644 index 0000000..67a6c00 --- /dev/null +++ b/src/test/java/capstone/relation/CustomInitListenerTest.java @@ -0,0 +1,26 @@ +package capstone.relation; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import capstone.relation.workspace.school.service.SchoolService; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = {TestCustomInitConfig.class}) // 테스트 전용 설정 클래스 추가 +@ActiveProfiles("test") +public class CustomInitListenerTest { + + @Autowired + private SchoolService schoolService; + + @Test + public void testCustomInitListener() { + // 테스트 로직 + // CustomInitListener가 호출되어 schoolService.initSchool() 메서드가 실행되는지 확인 + schoolService.initSchool(); + } +} diff --git a/src/test/java/capstone/relation/TestCustomInitConfig.java b/src/test/java/capstone/relation/TestCustomInitConfig.java new file mode 100644 index 0000000..92b0ca4 --- /dev/null +++ b/src/test/java/capstone/relation/TestCustomInitConfig.java @@ -0,0 +1,18 @@ +package capstone.relation; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; + +import capstone.relation.common.initializer.CustomInitListener; +import capstone.relation.workspace.school.service.SchoolService; + +@TestConfiguration +@Profile("test") +public class TestCustomInitConfig { + + @Bean + public CustomInitListener customInitListener(SchoolService schoolService) { + return new CustomInitListener(schoolService); + } +} From a4bbb7d337e45ec27c1ec771b272201270008412 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Fri, 26 Jul 2024 20:10:31 +0900 Subject: [PATCH 09/23] Refactor: MeetRoomService --- .../meeting/repository/RedisRepository.java | 2 +- .../meeting/service/MeetRoomService.java | 67 ++++++------------- .../user/controller/UserController.java | 2 +- 3 files changed, 24 insertions(+), 47 deletions(-) diff --git a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java index a5ba545..8cbc57a 100644 --- a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java +++ b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java @@ -30,7 +30,7 @@ public boolean isUserInRoom(String userId) { return userRoomMapping.get(USER_KEY, userId) != null; } - public String getUserRoom(String userId) { + public String getUserRoomId(String userId) { return userRoomMapping.get(USER_KEY, userId); } diff --git a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java index 88363b2..cfadfe8 100644 --- a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java +++ b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java @@ -5,14 +5,11 @@ import java.util.Set; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; -import capstone.relation.api.auth.exception.AuthException; import capstone.relation.common.util.SecurityUtil; import capstone.relation.meeting.domain.MeetRoom; import capstone.relation.meeting.dto.request.CreateRoomDto; @@ -24,7 +21,6 @@ import capstone.relation.user.UserService; import capstone.relation.user.dto.RoomInfoDto; import capstone.relation.user.dto.UserInfoDto; -import capstone.relation.websocket.SocketRegistry; import capstone.relation.workspace.WorkSpace; import capstone.relation.workspace.repository.WorkSpaceRepository; import lombok.RequiredArgsConstructor; @@ -37,48 +33,25 @@ public class MeetRoomService { private final WorkSpaceRepository workSpaceRepository; private final MeetRoomRepository meetRoomRepository; private final RedisRepository redisRepository; - private final SocketRegistry socketRegistry; private final SimpMessagingTemplate simpMessagingTemplate; @Transactional(readOnly = false) public JoinResponseDto createAndJoinRoom(CreateRoomDto createRoomDto) { - Long userId = SecurityUtil.getCurrentUserId(); String roomName = createRoomDto.getRoomName(); if (roomName == null || roomName.isEmpty()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "회의실 이름을 입력해주세요."); + Long userId = SecurityUtil.getCurrentUserId(); String workSpaceId = userService.getUserWorkSpaceId(userId); - try { - JoinResponseDto joinResponseDto = createAndJoin(workSpaceId, userId.toString(), roomName); - sendRoomList(workSpaceId); - return joinResponseDto; - } catch (AuthException e) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, e.getMessage()); - } catch (Exception e) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); - } - } - - public boolean isUserInRoom(String userId) { - return redisRepository.isUserInRoom(userId); - } - - public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { - if (!isUserInRoom(userId.toString())) - return new RoomInfoDto(); - - String roomId = redisRepository.getUserRoom(userId.toString()); - Set userIds = redisRepository.getRoomMembers(workspaceId, Long.parseLong(roomId)); - List userInfoList = userService.getUserInfoList(userIds); - return new RoomInfoDto(true, Long.parseLong(roomId), - meetRoomRepository.findById(Long.parseLong(roomId)).get().getRoomName(), userInfoList); + JoinResponseDto joinResponseDto = createAndJoin(workSpaceId, userId, roomName); + sendRoomList(workSpaceId); + return joinResponseDto; } - private JoinResponseDto createAndJoin(String workSpaceId, String userId, String roomName) { + private JoinResponseDto createAndJoin(String workSpaceId, Long userId, String roomName) { WorkSpace workSpace = workSpaceRepository.findById(workSpaceId) - .orElseThrow(() -> new IllegalArgumentException("Invalid workspace ID")); - if (isUserInRoom(userId)) + .orElseThrow(() -> new IllegalArgumentException("유저의 워크스페이스 정보가 없습니다.")); + if (redisRepository.isUserInRoom(userId.toString())) throw new IllegalArgumentException("User is already in the room: " + userId); - MeetRoom meetRoom = MeetRoom.builder() .roomName(roomName) .deleted(false) @@ -86,14 +59,25 @@ private JoinResponseDto createAndJoin(String workSpaceId, String userId, String meetRoomRepository.save(meetRoom); workSpace.addMeetRoom(meetRoom); Long roomId = meetRoom.getRoomId(); - return joinWorkspaceRoom(workSpaceId, userId, roomId); + return joinWorkspaceRoom(workSpaceId, userId.toString(), roomId); + } + + public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { + if (!redisRepository.isUserInRoom(userId.toString())) + throw new IllegalArgumentException("User is not in any room: " + userId); + + String roomId = redisRepository.getUserRoomId(userId.toString()); + Set userIds = redisRepository.getRoomMembers(workspaceId, Long.parseLong(roomId)); + List userInfoList = userService.getUserInfoList(userIds); + return new RoomInfoDto(true, Long.parseLong(roomId), + meetRoomRepository.findById(Long.parseLong(roomId)).get().getRoomName(), userInfoList); } public JoinResponseDto joinRoom(Long roomId) { Long userId = SecurityUtil.getCurrentUserId(); String workspaceId = userService.getUserWorkSpaceId(userId); - String meetRoom = redisRepository.getUserRoom(userId.toString()); - if (meetRoom != null) + String meetRoomId = redisRepository.getUserRoomId(userId.toString()); + if (meetRoomId != null) throw new IllegalArgumentException("User is already in the room: " + userId); JoinResponseDto joinResponseDto = joinWorkspaceRoom(workspaceId, userId.toString(), roomId); @@ -114,7 +98,7 @@ private JoinResponseDto joinWorkspaceRoom(String workSpaceId, String userId, Lon @Transactional(readOnly = false) public void leaveRoom(Long userId) { String workspaceId = userService.getUserWorkSpaceId(userId); - String meetRoom = redisRepository.getUserRoom(userId.toString()); + String meetRoom = redisRepository.getUserRoomId(userId.toString()); if (meetRoom == null) return; redisRepository.removeUserFromRoom(workspaceId, Long.parseLong(meetRoom), userId.toString()); @@ -155,11 +139,4 @@ private void sendUserList(String workSpaceId, Long roomId) { simpMessagingTemplate.convertAndSend("/topic/meetingRoom/" + roomId + "/users", userInfoList); } - public void sendErrorMessage(SimpMessageHeaderAccessor headerAccessor, String message, String destination, - int status) { - Long userId = (Long)headerAccessor.getSessionAttributes().get("userId"); - String socketId = socketRegistry.getSocketId(userId.toString()); - simpMessagingTemplate.convertAndSendToUser(socketId, destination, - ResponseEntity.status(status).body(message)); - } } diff --git a/src/main/java/capstone/relation/user/controller/UserController.java b/src/main/java/capstone/relation/user/controller/UserController.java index 85fd717..2362aec 100644 --- a/src/main/java/capstone/relation/user/controller/UserController.java +++ b/src/main/java/capstone/relation/user/controller/UserController.java @@ -28,7 +28,7 @@ public UserInfoDto getInfo() { } @GetMapping("/room/info") - @Operation(summary = "사용자 방 정보 조회", description = "현재 로그인한 사용자의 방 정보를 조회합니다.") + @Operation(summary = "사용자 회의 방 정보 조회", description = "현재 로그인한 사용자의 회의 방 정보를 조회합니다.") public RoomInfoDto getRoomInfo() { User user = userService.getUserEntity(); Long userId = user.getId(); From f5aa6c9cec887ddac8871783609b5a9e077a5264 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Fri, 26 Jul 2024 20:10:49 +0900 Subject: [PATCH 10/23] Feat: IllegalArgumentException Throw --- .../relation/common/error/GlobalExceptionHandler.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/capstone/relation/common/error/GlobalExceptionHandler.java b/src/main/java/capstone/relation/common/error/GlobalExceptionHandler.java index cdcbe7c..e0cb62f 100644 --- a/src/main/java/capstone/relation/common/error/GlobalExceptionHandler.java +++ b/src/main/java/capstone/relation/common/error/GlobalExceptionHandler.java @@ -18,6 +18,17 @@ public ResponseEntity handleResponseStatusException(ResponseStatusExcept return new ResponseEntity<>(ex.getReason(), ex.getStatusCode()); } + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException ex, + HttpServletRequest request) { + ProblemDetail problemDetail = ProblemDetailCreator.create(ex, request, HttpStatus.BAD_REQUEST); + problemDetail.setTitle("Invalid Argument"); + problemDetail.setStatus(HttpStatus.BAD_REQUEST.value()); + problemDetail.setDetail(ex.getMessage()); + + return ResponseEntity.badRequest().body(problemDetail); + } + @ExceptionHandler(Exception.class) public ResponseEntity handleAllException(Exception ex, HttpServletRequest request) { ProblemDetail problemDetail = ProblemDetailCreator.create(ex, request, HttpStatus.BAD_REQUEST); From 1533cdca4444e4bcc06481cff102e5cf092e797e Mon Sep 17 00:00:00 2001 From: hawardShin Date: Fri, 26 Jul 2024 20:12:43 +0900 Subject: [PATCH 11/23] Feat: MeetRoomServiceTest --- .../meeting/service/MeetRoomServiceTest.java | 167 ++++++------------ 1 file changed, 56 insertions(+), 111 deletions(-) diff --git a/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java b/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java index 2f000a1..ab4e07d 100644 --- a/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java +++ b/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java @@ -3,176 +3,121 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.BDDMockito.*; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; import java.util.Optional; -import java.util.Set; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.redis.core.HashOperations; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.messaging.simp.SimpMessageHeaderAccessor; +import org.springframework.http.HttpStatus; import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.web.server.ResponseStatusException; import capstone.relation.meeting.domain.MeetRoom; import capstone.relation.meeting.dto.request.CreateRoomDto; import capstone.relation.meeting.dto.response.JoinResponseDto; import capstone.relation.meeting.dto.response.MeetingRoomListDto; import capstone.relation.meeting.repository.MeetRoomRepository; -import capstone.relation.user.domain.Role; -import capstone.relation.user.domain.User; +import capstone.relation.meeting.repository.RedisRepository; +import capstone.relation.security.WithMockCustomUser; +import capstone.relation.user.UserService; import capstone.relation.user.repository.UserRepository; -import capstone.relation.websocket.SocketRegistry; import capstone.relation.workspace.WorkSpace; import capstone.relation.workspace.repository.WorkSpaceRepository; -@ExtendWith(MockitoExtension.class) +@ExtendWith(SpringExtension.class) class MeetRoomServiceTest { @InjectMocks private MeetRoomService meetRoomService; @Mock - private SimpMessagingTemplate simpMessagingTemplate; + private UserService mockUserService; @Mock - private SocketRegistry socketRegistry; + private SimpMessagingTemplate mockSimpMessagingTemplate; @Mock - private WorkSpaceRepository workSpaceRepository; + private WorkSpaceRepository mockWorkSpaceRepository; @Mock - private MeetRoomRepository meetRoomRepository; + private MeetRoomRepository mockMeetRoomRepository; - @Mock - private RedisTemplate redisTemplate; - - @Mock - private HashOperations>> workspaceRoomParticipants; //workspaceId, roomId, userIds + @Mock - private HashOperations mockUserRoomMapping; + private UserRepository mockUserRepository; @Mock - private UserRepository userRepository; - - @BeforeEach - void setUp() { - // RedisTemplate의 opsForHash를 모킹하여 hashOperations를 반환하도록 설정합니다. - when(redisTemplate.opsForHash()).thenReturn((HashOperations)workspaceRoomParticipants) - .thenReturn(mockUserRoomMapping); - // MeetingService의 init() 메서드를 명시적으로 호출합니다. - meetRoomService.init(); - } + private RedisRepository mockRedisRepository; - @DisplayName("회의 방을 생성할 수 있다.") + @DisplayName("회의 방을 생성하고 가입할 수 있다..") + @WithMockCustomUser @Test - void createRoom() { + void createAndJoinRoom() { // given CreateRoomDto createRoomDto = new CreateRoomDto("테스트 방이름"); - SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(); - Map sessionAttributes = new HashMap<>(); - sessionAttributes.put("userId", 1L); - sessionAttributes.put("workSpaceId", "workspace-1"); - headerAccessor.setSessionAttributes(sessionAttributes); - - given(workSpaceRepository.findById("workspace-1")).willReturn(Optional.of(new WorkSpace())); - given(meetRoomRepository.save(any(MeetRoom.class))).willAnswer(invocation -> { + + given(mockUserService.getUserWorkSpaceId(1L)).willReturn("workspace-1"); + given(mockWorkSpaceRepository.findById("workspace-1")).willReturn(Optional.of(new WorkSpace())); + given(mockMeetRoomRepository.save(any(MeetRoom.class))).willAnswer(invocation -> { MeetRoom meetRoom = invocation.getArgument(0); meetRoom.setRoomId(1L); return meetRoom; }); - given(workspaceRoomParticipants.get(anyString(), anyString())).willReturn(new HashMap<>()); - given(meetRoomRepository.findById(1L)).willReturn( + given(mockMeetRoomRepository.findById(1L)).willReturn( Optional.of(MeetRoom.builder().roomId(1L).roomName("테스트 방이름").build())); // when - JoinResponseDto joinResponse = meetRoomService.createAndJoinRoom(createRoomDto, 1L, "workspace-1"); + JoinResponseDto joinResponse = meetRoomService.createAndJoinRoom(createRoomDto); // then - verify(simpMessagingTemplate, times(1)).convertAndSend(eq("/topic/workspace-1/meetingRoomList"), + verify(mockSimpMessagingTemplate, times(1)).convertAndSend(eq("/topic/workspace-1/meetingRoomList"), any(MeetingRoomListDto.class)); assertThat(joinResponse).isNotNull(); assertThat(joinResponse.getRoomName()).isEqualTo("테스트 방이름"); assertThat(joinResponse.getRoomId()).isEqualTo(1L); } - @DisplayName("회의 방 목록을 받아올 수 있다.") + @DisplayName("잘못된 회의실 이름으로 회의 방을 생성하려고 할 때 예외가 발생한다.") @Test - void getRoomList() { + public void createAndJoinRoomWithInvalidRoomName() { // given - WorkSpace workSpace = new WorkSpace(); - workSpace.setId("workspace-1"); - String workSpaceId = workSpace.getId(); - MeetRoom meetRoom1 = MeetRoom.builder().roomId(1L).roomName("회의실1").deleted(false).workSpace(workSpace).build(); - MeetRoom meetRoom2 = MeetRoom.builder().roomId(2L).roomName("회의실2").deleted(false).workSpace(workSpace).build(); - - Set meetRooms = new HashSet<>(); - meetRooms.add(meetRoom1); - meetRooms.add(meetRoom2); - Set userIds = new HashSet<>(); - userIds.add("1"); - userIds.add("2"); - HashMap> mockParti = new HashMap<>(); - mockParti.put("1", userIds); - mockParti.put("2", userIds); - given(meetRoomRepository.findAllByWorkSpaceId(workSpaceId)).willReturn(meetRooms); - given(workspaceRoomParticipants.get(anyString(), anyString())).willReturn(mockParti); - - // when - meetRoomService.sendRoomList(workSpaceId); - - // then - ArgumentCaptor roomListCaptor = ArgumentCaptor.forClass(MeetingRoomListDto.class); - verify(simpMessagingTemplate, times(1)).convertAndSend(eq("/topic/workspace-1/meetingRoomList"), - roomListCaptor.capture()); - - MeetingRoomListDto roomList = roomListCaptor.getValue(); - assertThat(roomList).isNotNull(); - assertThat(roomList.getMeetingRoomList()).hasSize(2); - assertThat(roomList.getMeetingRoomList()).anyMatch( - room -> room.getRoomId().equals(1L) && room.getRoomName().equals("회의실1")); - assertThat(roomList.getMeetingRoomList()).anyMatch( - room -> room.getRoomId().equals(2L) && room.getRoomName().equals("회의실2")); + CreateRoomDto createRoomDto = new CreateRoomDto(""); + + // when & then + assertThatThrownBy(() -> meetRoomService.createAndJoinRoom(createRoomDto)) + .isInstanceOf(ResponseStatusException.class) + .satisfies(exception -> { + ResponseStatusException ex = (ResponseStatusException)exception; + assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(ex.getReason()).isEqualTo("회의실 이름을 입력해주세요."); + }); } - @DisplayName("회의 방에 참여할 수 있다.") + @DisplayName("이미 회의실에 참여한 사용자가 다시 참여하려고 할 때 예외가 발생한다.") + @WithMockCustomUser @Test - void joinRoom() { + public void createAndJoinRoomWithAlreadyJoinedUser() { // given - SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(); - Map sessionAttributes = new HashMap<>(); - sessionAttributes.put("userId", 1L); - sessionAttributes.put("workSpaceId", "workspace-1"); - headerAccessor.setSessionAttributes(sessionAttributes); - - WorkSpace workSpace = new WorkSpace(); - workSpace.setId("workspace-1"); - MeetRoom meetRoom1 = MeetRoom.builder().roomId(1L).roomName("회의실1").deleted(false).workSpace(workSpace).build(); - given(meetRoomRepository.findById(1L)).willReturn(Optional.of(meetRoom1)); - given(userRepository.findById(1L)).willReturn(Optional.of( - User.builder() - .id(1L) - .email("wnddms12345@naver.com") - .profileImage("https://avatars.githubusercontent.com/u/77449538?v=4") - .userName("김민수") - .provider("github") - .role(Role.USER) - .build())); - given(workspaceRoomParticipants.get(anyString(), anyString())).willReturn(new HashMap<>()); - // when - JoinResponseDto joinResponseDto = meetRoomService.joinRoom(1L, "workspace-1", 1L); + CreateRoomDto createRoomDto = new CreateRoomDto("테스트 방이름"); - // then - assertThat(joinResponseDto).isNotNull(); - assertThat(joinResponseDto.getRoomId()).isEqualTo(1L); - assertThat(joinResponseDto.getRoomName()).isEqualTo("회의실1"); + given(mockUserService.getUserWorkSpaceId(1L)).willReturn("workspace-1"); + given(mockWorkSpaceRepository.findById("workspace-1")).willReturn(Optional.of(new WorkSpace())); + given(mockRedisRepository.isUserInRoom("1")).willReturn(true); + + // when & then + assertThatThrownBy(() -> meetRoomService.createAndJoinRoom(createRoomDto)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("User is already in the room: 1"); + } + + @DisplayName("회의 방의 정보를 받아올 수 있다.") + @WithMockCustomUser + @Test + void getRoomInfo() { + //given + given(mockUserService.getUserWorkSpaceId(1L)).willReturn("workspace-1"); } } From 996d24048357032c8e467cfdd33cd41f1579ab5e Mon Sep 17 00:00:00 2001 From: hawardShin Date: Fri, 26 Jul 2024 21:42:14 +0900 Subject: [PATCH 12/23] Feat: MeetRoomServiceTest --- .../meeting/service/MeetRoomServiceTest.java | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java b/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java index ab4e07d..cf14c6f 100644 --- a/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java +++ b/src/test/java/capstone/relation/meeting/service/MeetRoomServiceTest.java @@ -23,7 +23,6 @@ import capstone.relation.meeting.repository.RedisRepository; import capstone.relation.security.WithMockCustomUser; import capstone.relation.user.UserService; -import capstone.relation.user.repository.UserRepository; import capstone.relation.workspace.WorkSpace; import capstone.relation.workspace.repository.WorkSpaceRepository; @@ -43,16 +42,11 @@ class MeetRoomServiceTest { @Mock private MeetRoomRepository mockMeetRoomRepository; - - //workspaceId, roomId, userIds - - @Mock - private UserRepository mockUserRepository; - + @Mock private RedisRepository mockRedisRepository; - @DisplayName("회의 방을 생성하고 가입할 수 있다..") + @DisplayName("회의 방을 생성하고 가입할 수 있다.") @WithMockCustomUser @Test void createAndJoinRoom() { @@ -105,7 +99,7 @@ public void createAndJoinRoomWithAlreadyJoinedUser() { given(mockUserService.getUserWorkSpaceId(1L)).willReturn("workspace-1"); given(mockWorkSpaceRepository.findById("workspace-1")).willReturn(Optional.of(new WorkSpace())); - given(mockRedisRepository.isUserInRoom("1")).willReturn(true); + given(mockRedisRepository.isUserInRoom(1L)).willReturn(true); // when & then assertThatThrownBy(() -> meetRoomService.createAndJoinRoom(createRoomDto)) @@ -113,11 +107,33 @@ public void createAndJoinRoomWithAlreadyJoinedUser() { .hasMessage("User is already in the room: 1"); } - @DisplayName("회의 방의 정보를 받아올 수 있다.") + @DisplayName("워크스페이스에 참여한 모든 유저에게 회의실 목록을 전송할 수 있다.") + @Test + public void sendRoomList() { + // given + given(mockWorkSpaceRepository.findById("workspace-1")).willReturn(Optional.of(new WorkSpace())); + + // when + meetRoomService.sendRoomList("workspace-1"); + + // then + verify(mockSimpMessagingTemplate, times(1)).convertAndSend(eq("/topic/workspace-1/meetingRoomList"), + any(MeetingRoomListDto.class)); + } + + @DisplayName("회의실을 나갈 수 있다.") @WithMockCustomUser @Test - void getRoomInfo() { - //given + public void leaveRoom() { + // given given(mockUserService.getUserWorkSpaceId(1L)).willReturn("workspace-1"); + given(mockRedisRepository.isUserInRoom(1L)).willReturn(true); + given(mockRedisRepository.getUserRoomId(1L)).willReturn("1"); + + // when + meetRoomService.leaveRoom(1L); + + // then + verify(mockRedisRepository, times(1)).removeUserFromRoom("workspace-1", 1L, "1"); } } From c536db9cc3502e2eb26f1898322b844335581127 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Fri, 26 Jul 2024 21:42:37 +0900 Subject: [PATCH 13/23] Refactor: MeetRoomService with comment --- .../meeting/service/MeetRoomService.java | 131 +++++++++++++----- 1 file changed, 96 insertions(+), 35 deletions(-) diff --git a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java index cfadfe8..eb7d867 100644 --- a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java +++ b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java @@ -1,6 +1,6 @@ package capstone.relation.meeting.service; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -21,8 +21,6 @@ import capstone.relation.user.UserService; import capstone.relation.user.dto.RoomInfoDto; import capstone.relation.user.dto.UserInfoDto; -import capstone.relation.workspace.WorkSpace; -import capstone.relation.workspace.repository.WorkSpaceRepository; import lombok.RequiredArgsConstructor; @Service @@ -30,11 +28,15 @@ @Transactional(readOnly = true) public class MeetRoomService { private final UserService userService; - private final WorkSpaceRepository workSpaceRepository; - private final MeetRoomRepository meetRoomRepository; private final RedisRepository redisRepository; + private final MeetRoomRepository meetRoomRepository; private final SimpMessagingTemplate simpMessagingTemplate; + /** + * 미팅룸을 생성하고 참여합니다. + * @param createRoomDto 생성할 미팅룸 정보 DTO + * @return 참여 응답 DTO + */ @Transactional(readOnly = false) public JoinResponseDto createAndJoinRoom(CreateRoomDto createRoomDto) { String roomName = createRoomDto.getRoomName(); @@ -42,101 +44,160 @@ public JoinResponseDto createAndJoinRoom(CreateRoomDto createRoomDto) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "회의실 이름을 입력해주세요."); Long userId = SecurityUtil.getCurrentUserId(); String workSpaceId = userService.getUserWorkSpaceId(userId); - JoinResponseDto joinResponseDto = createAndJoin(workSpaceId, userId, roomName); + + // 미팅룸을 생성, 참여, 미팅룸 목록을 전송합니다. + Long roomId = createRoom(userId, roomName); + JoinResponseDto joinResponseDto = joinWorkspaceRoom(workSpaceId, userId, roomId); sendRoomList(workSpaceId); return joinResponseDto; } - private JoinResponseDto createAndJoin(String workSpaceId, Long userId, String roomName) { - WorkSpace workSpace = workSpaceRepository.findById(workSpaceId) - .orElseThrow(() -> new IllegalArgumentException("유저의 워크스페이스 정보가 없습니다.")); - if (redisRepository.isUserInRoom(userId.toString())) + /** + * 미팅룸을 생성합니다. + * @param userId 사용자 ID + * @param roomName 미팅룸 이름 + * @return 생성된 미팅룸 ID + */ + private Long createRoom(Long userId, String roomName) { + if (redisRepository.isUserInRoom(userId)) throw new IllegalArgumentException("User is already in the room: " + userId); MeetRoom meetRoom = MeetRoom.builder() .roomName(roomName) .deleted(false) .build(); meetRoomRepository.save(meetRoom); - workSpace.addMeetRoom(meetRoom); - Long roomId = meetRoom.getRoomId(); - return joinWorkspaceRoom(workSpaceId, userId.toString(), roomId); + return meetRoom.getRoomId(); } + /** + * 미팅룸 정보를 조회합니다. + * @param workspaceId 워크스페이스 ID + * @param userId 사용자 ID + * @return 미팅룸 정보 DTO + */ public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { - if (!redisRepository.isUserInRoom(userId.toString())) + if (!redisRepository.isUserInRoom(userId)) throw new IllegalArgumentException("User is not in any room: " + userId); - String roomId = redisRepository.getUserRoomId(userId.toString()); - Set userIds = redisRepository.getRoomMembers(workspaceId, Long.parseLong(roomId)); + Long roomId = Long.parseLong(redisRepository.getUserRoomId(userId)); + Set userIds = redisRepository.getRoomMembers(workspaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); - return new RoomInfoDto(true, Long.parseLong(roomId), - meetRoomRepository.findById(Long.parseLong(roomId)).get().getRoomName(), userInfoList); + return new RoomInfoDto(true, roomId, getRoomName(roomId), userInfoList); + } + + /** + * 미팅룸 이름을 조회합니다. + * @param roomId 미팅룸 ID + * @return 미팅룸 이름 + */ + private String getRoomName(Long roomId) { + return meetRoomRepository.findById(roomId) + .orElseThrow(() -> new IllegalArgumentException("Invalid room ID : " + roomId)) + .getRoomName(); } + /** + * 미팅룸에 참여합니다. 컨트롤러 연결 메서드 + * @param roomId 가입하고자 하는 미팅룸 ID + * @return 참여 응답 DTO + */ public JoinResponseDto joinRoom(Long roomId) { Long userId = SecurityUtil.getCurrentUserId(); String workspaceId = userService.getUserWorkSpaceId(userId); - String meetRoomId = redisRepository.getUserRoomId(userId.toString()); + String meetRoomId = redisRepository.getUserRoomId(userId); if (meetRoomId != null) throw new IllegalArgumentException("User is already in the room: " + userId); - JoinResponseDto joinResponseDto = joinWorkspaceRoom(workspaceId, userId.toString(), roomId); + JoinResponseDto joinResponseDto = joinWorkspaceRoom(workspaceId, userId, roomId); sendRoomList(workspaceId); return joinResponseDto; } - private JoinResponseDto joinWorkspaceRoom(String workSpaceId, String userId, Long roomId) { + /** + * 미팅룸에 참여합니다. + * @param workSpaceId 워크스페이스 ID + * @param userId 사용자 ID + * @param roomId 미팅룸 ID + * @return 참여 응답 DTO + */ + private JoinResponseDto joinWorkspaceRoom(String workSpaceId, Long userId, Long roomId) { MeetRoom meetRoom = meetRoomRepository.findById(roomId) .orElseThrow(() -> new IllegalArgumentException("Invalid room ID")); - redisRepository.addUserToRoom(workSpaceId, roomId, userId); + redisRepository.addUserToRoom(workSpaceId, roomId, userId.toString()); Set userIds = redisRepository.getRoomMembers(workSpaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); sendUserList(workSpaceId, roomId); return new JoinResponseDto(roomId, meetRoom.getRoomName(), userInfoList, (long)userIds.size()); } + /** + * 미팅룸에서 나갑니다. + * @param userId 사용자 ID + */ @Transactional(readOnly = false) public void leaveRoom(Long userId) { String workspaceId = userService.getUserWorkSpaceId(userId); - String meetRoom = redisRepository.getUserRoomId(userId.toString()); - if (meetRoom == null) + String meetRoomId = redisRepository.getUserRoomId(userId); + if (meetRoomId == null) return; - redisRepository.removeUserFromRoom(workspaceId, Long.parseLong(meetRoom), userId.toString()); - sendUserList(workspaceId, Long.parseLong(meetRoom)); + redisRepository.removeUserFromRoom(workspaceId, Long.parseLong(meetRoomId), userId.toString()); + sendUserList(workspaceId, Long.parseLong(meetRoomId)); sendRoomList(workspaceId); } + /** + * 미팅룸 목록을 웹소켓을 통해 전송합니다. + * 이벤트 : /topic/{workSpaceId}/meetingRoomList + * 전송 데이터 : 워크스페이스에 있는 미팅룸 목록 + * @param workSpaceId 참여중인 워크스페이스 ID + * @return 미팅룸 목록 + */ public MeetingRoomListDto sendRoomList(String workSpaceId) { + // 미팅룸 정보를 조회합니다. Set meetRooms = meetRoomRepository.findAllByWorkSpaceId(workSpaceId); MeetingRoomListDto meetingRoomListDto = new MeetingRoomListDto(); + // 미팅룸 정보를 DTO로 변환합니다. for (MeetRoom meetRoom : meetRooms) { - Long roomId = meetRoom.getRoomId(); MeetingRoomDto meetingRoomDto = new MeetingRoomDto(); - meetingRoomDto.setRoomId(roomId); + meetingRoomDto.setRoomId(meetRoom.getRoomId()); meetingRoomDto.setRoomName(meetRoom.getRoomName()); - Set roomMembers = redisRepository.getRoomMembers(workSpaceId, roomId); - if (roomMembers == null) { - return meetingRoomListDto; - } + + // 미팅룸에 참여한 사용자 수를 조회합니다. + Set roomMembers = redisRepository.getRoomMembers(workSpaceId, meetRoom.getRoomId()); meetingRoomDto.setUserCount(roomMembers.size()); - List userInfoList = new ArrayList<>(); - meetingRoomDto.setUserInfoList(userInfoList); + + // 빈 사용자 정보 리스트를 생성합니다. + meetingRoomDto.setUserInfoList(Collections.emptyList()); meetingRoomListDto.getMeetingRoomList().add(meetingRoomDto); } + + // 미팅룸 정보를 전송합니다.(웹소켓) simpMessagingTemplate.convertAndSend("/topic/" + workSpaceId + "/meetingRoomList", meetingRoomListDto); return meetingRoomListDto; } + /** + * 미팅룸 목록을 웹소켓을 통해서 전송합니다.(컨트롤러 연결 메서드) + * 이벤트 : /topic/meetingRoom/{roomId}/users + * 전송 데이터 : 사용자 정보 리스트 + * @return 미팅룸 목록 + */ public MeetingRoomListDto sendRoomList() { Long userId = SecurityUtil.getCurrentUserId(); String workSpaceId = userService.getUserWorkSpaceId(userId); return sendRoomList(workSpaceId); } + /** + * 미팅룸에 참여한 사용자 목록을 웹소켓을 통해서 전송합니다. + * 이벤트 : /topic/meetingRoom/{roomId}/users + * 전송 데이터 : 사용자 정보 리스트 + * @param workSpaceId 워크스페이스 ID + * @param roomId 미팅룸 ID + */ private void sendUserList(String workSpaceId, Long roomId) { Set userIds = redisRepository.getRoomMembers(workSpaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); simpMessagingTemplate.convertAndSend("/topic/meetingRoom/" + roomId + "/users", userInfoList); } - } From 0def8e3e77a410a8e96e4e8b353dc5ec3580fea5 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Fri, 26 Jul 2024 21:42:53 +0900 Subject: [PATCH 14/23] Refactor: Change UserId to Long --- .../relation/meeting/repository/RedisRepository.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java index 8cbc57a..1c96822 100644 --- a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java +++ b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java @@ -26,12 +26,12 @@ protected void init() { userRoomMapping = redisTemplate.opsForHash(); } - public boolean isUserInRoom(String userId) { - return userRoomMapping.get(USER_KEY, userId) != null; + public boolean isUserInRoom(Long userId) { + return userRoomMapping.get(USER_KEY, userId.toString()) != null; } - public String getUserRoomId(String userId) { - return userRoomMapping.get(USER_KEY, userId); + public String getUserRoomId(Long userId) { + return userRoomMapping.get(USER_KEY, userId.toString()); } public void addUserToRoom(String workspaceId, Long roomId, String userId) { From 1e3834f90fcee8f6e4de41ae577441d54a216d2e Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 27 Jul 2024 19:05:55 +0900 Subject: [PATCH 15/23] Feat: MeetRoomIntegrationTest --- .../integration/MeetRoomIntegrationTest.java | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 src/test/java/capstone/relation/meeting/integration/MeetRoomIntegrationTest.java diff --git a/src/test/java/capstone/relation/meeting/integration/MeetRoomIntegrationTest.java b/src/test/java/capstone/relation/meeting/integration/MeetRoomIntegrationTest.java new file mode 100644 index 0000000..c4a09f5 --- /dev/null +++ b/src/test/java/capstone/relation/meeting/integration/MeetRoomIntegrationTest.java @@ -0,0 +1,224 @@ +package capstone.relation.meeting.integration; + +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import capstone.relation.meeting.domain.MeetRoom; +import capstone.relation.meeting.dto.request.CreateRoomDto; +import capstone.relation.meeting.repository.MeetRoomRepository; +import capstone.relation.meeting.repository.RedisRepository; +import capstone.relation.security.WithMockCustomUser; +import capstone.relation.user.domain.Role; +import capstone.relation.user.domain.User; +import capstone.relation.user.repository.UserRepository; +import capstone.relation.workspace.WorkSpace; +import capstone.relation.workspace.repository.WorkSpaceRepository; +import capstone.relation.workspace.school.domain.School; +import capstone.relation.workspace.school.respository.SchoolRepository; + +@ExtendWith(SpringExtension.class) +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles("test") +@DisplayName("회의방 통합 테스트") +public class MeetRoomIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + UserRepository userRepository; + + @Autowired + WorkSpaceRepository workSpaceRepository; + + @Autowired + SchoolRepository schoolRepository; + + @Autowired + MeetRoomRepository meetRoomRepository; + + @MockBean + private RedisRepository redisRepository; + + private WorkSpace testWorkSpace; + private School testSchool; + + @BeforeEach + void setup() { + redisRepository.deleteAll(); // Redis 저장소 초기화 + testSchool = new School("서울캠", "과기대", "4년제", "링크", "학교소개", "학교위치", "학교지역", "학교종류", new HashSet<>()); + schoolRepository.save(testSchool); + + testWorkSpace = new WorkSpace(); + testWorkSpace.setName("testWorkSpace"); + testWorkSpace.setSchool(testSchool); + workSpaceRepository.save(testWorkSpace); + + addTestUser("testUser1"); + addTestUser("testUser2"); + addTestUser("testUser3"); + + // RedisRepository 모킹 + when(redisRepository.isUserInRoom(any())).thenReturn(false); + when(redisRepository.getUserRoomId(1L)).thenReturn(null); + when(redisRepository.getRoomMemberIds(testWorkSpace.getId(), 1L)).thenReturn(new HashSet<>(Set.of("1"))); + when(redisRepository.getRoomMemberIds(testWorkSpace.getId(), 2L)).thenReturn(new HashSet<>(Set.of("1"))); + } + + private void addTestUser(String username) { + User user = User.builder().userName(username) + .email(username + "@naver.com") + .profileImage( + "https://lh3.googleusercontent.com/ogw/AF2bZyhqowurXq6imx61oPHn5G_c6OIEnucOyJanitxYGFUI498=s32-c-mo") + .role(Role.USER) + .provider("naver") + .build(); + workSpaceRepository.save(testWorkSpace); + user.setWorkSpace(testWorkSpace); + userRepository.save(user); + } + + @Test + @DisplayName("통합 테스트 : 새로운 방 생성을 할 수 있다.") + @WithMockCustomUser + @DirtiesContext + void createRoom() throws Exception { + // given + CreateRoomDto createRoomDto = new CreateRoomDto("New Room"); + String requestJson = objectMapper.writeValueAsString(createRoomDto); + + // when & then + mockMvc.perform(post("/meet/room/create") + .contentType("application/json") + .content(requestJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.roomId").isNumber()) + .andExpect(jsonPath("$.roomName").value("New Room")) + .andExpect(jsonPath("$.userInfoList").isArray()) + .andExpect(jsonPath("$.userCount").value(1)); + } + + @Test + @DisplayName("통합 테스트 : 존재하는 회의방에 참여할 수 있다.") + @WithMockCustomUser + @DirtiesContext + void joinRoom() throws Exception { + // given + MeetRoom meetRoom = MeetRoom.builder() + .roomName("채팅방") + .deleted(false) + .build(); + meetRoomRepository.save(meetRoom); + // when & then + mockMvc.perform(post("/meet/room/join/1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.roomId").isNumber()) + .andExpect(jsonPath("$.roomName").value("채팅방")) + .andExpect(jsonPath("$.userInfoList").isArray()) + .andExpect(jsonPath("$.userCount").value(1)); + } + + @Test + @DisplayName("통합 테스트 : 존재하지 않는 회의방에 참여할 수 없다.") + @DirtiesContext + @WithMockCustomUser + void joinRoomFail() throws Exception { + // when & then + mockMvc.perform(post("/meet/room/join/1")) + .andExpect(status().isNotFound()); + } + + @Test + @DisplayName("통합 테스트 : 회의방에서 나갈 수 있다.") + @WithMockCustomUser(id = 2L) + @DirtiesContext + void leaveRoom() throws Exception { + // given + MeetRoom meetRoom = MeetRoom.builder() + .roomName("채팅방1") + .deleted(false) + .build(); + meetRoomRepository.save(meetRoom); + MeetRoom meetRoom2 = MeetRoom.builder() + .roomName("채팅방2") + .deleted(false) + .build(); + meetRoomRepository.save(meetRoom2); + // join 시에 필요한 모킹 설정 + when(redisRepository.getUserRoomId(anyLong())).thenReturn(null); + when(redisRepository.getRoomMemberIds(any(), anyLong())).thenReturn(new HashSet<>(Set.of("2"))); + // when & then + mockMvc.perform(post("/meet/room/join/2")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.roomId").isNumber()) + .andExpect(jsonPath("$.roomName").value("채팅방2")) + .andExpect(jsonPath("$.userInfoList").isArray()) + .andExpect(jsonPath("$.userCount").value(1)); + // leave 시에 필요한 모킹 설정 + when(redisRepository.getUserRoomId(anyLong())).thenReturn(meetRoom2.getRoomId().toString()); + + mockMvc.perform(post("/meet/room/leave")) + .andExpect(status().isOk()); + } + + @Test + @DisplayName("통합 테스트 : 회의방 목록을 요청할 수 있다.") + @WithMockCustomUser + @DirtiesContext + void requestRoomList() throws Exception { + // given + MeetRoom meetRoom = MeetRoom.builder() + .roomName("채팅방1") + .deleted(false) + .build(); + meetRoomRepository.save(meetRoom); + MeetRoom meetRoom2 = MeetRoom.builder() + .roomName("채팅방2") + .deleted(false) + .build(); + meetRoomRepository.save(meetRoom2); + + testWorkSpace.addMeetRoom(meetRoom); + testWorkSpace.addMeetRoom(meetRoom2); + workSpaceRepository.save(testWorkSpace); + + // join 시에 필요한 모킹 설정 + when(redisRepository.getUserRoomId(anyLong())).thenReturn(null); + when(redisRepository.getRoomMemberIds(any(), anyLong())).thenReturn(new HashSet<>(Set.of("1"))); + // when & then + mockMvc.perform(get("/meet/room/list")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.meetingRoomList").isArray()) + .andExpect(jsonPath("$.meetingRoomList[0].roomId").isNumber()) + .andExpect(jsonPath("$.meetingRoomList[0].roomName").value("채팅방1")) + .andExpect(jsonPath("$.meetingRoomList[0].userCount").value(1)) + .andExpect(jsonPath("$.meetingRoomList[0].userInfoList").isArray()) + .andExpect(jsonPath("$.meetingRoomList[1].roomId").isNumber()) + .andExpect(jsonPath("$.meetingRoomList[1].roomName").value("채팅방2")) + .andExpect(jsonPath("$.meetingRoomList[1].userCount").value(1)) + .andExpect(jsonPath("$.meetingRoomList[1].userInfoList").isArray()); + } +} From 425be2de3e791b0dd5c38eecf32af9761b87005f Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 27 Jul 2024 19:07:15 +0900 Subject: [PATCH 16/23] =?UTF-8?q?Refactor:=20=EB=AA=85=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20Id=20=EB=AA=A9=EB=A1=9D=EC=9D=84=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/repository/RedisRepository.java | 7 +++++- .../meeting/service/MeetRoomService.java | 23 +++++++++++-------- .../relation/workspace/WorkSpace.java | 6 +---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java index 1c96822..6df6288 100644 --- a/src/main/java/capstone/relation/meeting/repository/RedisRepository.java +++ b/src/main/java/capstone/relation/meeting/repository/RedisRepository.java @@ -68,11 +68,16 @@ public void removeUserFromRoom(String workspaceId, Long roomId, String userId) { } - public Set getRoomMembers(String workspaceId, Long roomId) { + public Set getRoomMemberIds(String workspaceId, Long roomId) { HashMap> roomParticipants = workspaceRoomParticipants.get(WORK_KEY, workspaceId); if (roomParticipants == null) { return new HashSet<>(); } return roomParticipants.get(roomId.toString()); } + + public void deleteAll() { + redisTemplate.delete(WORK_KEY); + redisTemplate.delete(USER_KEY); + } } diff --git a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java index eb7d867..c609d84 100644 --- a/src/main/java/capstone/relation/meeting/service/MeetRoomService.java +++ b/src/main/java/capstone/relation/meeting/service/MeetRoomService.java @@ -1,6 +1,5 @@ package capstone.relation.meeting.service; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -80,7 +79,7 @@ public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { throw new IllegalArgumentException("User is not in any room: " + userId); Long roomId = Long.parseLong(redisRepository.getUserRoomId(userId)); - Set userIds = redisRepository.getRoomMembers(workspaceId, roomId); + Set userIds = redisRepository.getRoomMemberIds(workspaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); return new RoomInfoDto(true, roomId, getRoomName(roomId), userInfoList); } @@ -92,7 +91,7 @@ public RoomInfoDto getRoomInfo(String workspaceId, Long userId) { */ private String getRoomName(Long roomId) { return meetRoomRepository.findById(roomId) - .orElseThrow(() -> new IllegalArgumentException("Invalid room ID : " + roomId)) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Invalid room ID : " + roomId)) .getRoomName(); } @@ -115,6 +114,7 @@ public JoinResponseDto joinRoom(Long roomId) { /** * 미팅룸에 참여합니다. + * 웹소켓으로 변경된 사용자 목록을 전송합니다. * @param workSpaceId 워크스페이스 ID * @param userId 사용자 ID * @param roomId 미팅룸 ID @@ -122,9 +122,9 @@ public JoinResponseDto joinRoom(Long roomId) { */ private JoinResponseDto joinWorkspaceRoom(String workSpaceId, Long userId, Long roomId) { MeetRoom meetRoom = meetRoomRepository.findById(roomId) - .orElseThrow(() -> new IllegalArgumentException("Invalid room ID")); + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Invalid room ID")); redisRepository.addUserToRoom(workSpaceId, roomId, userId.toString()); - Set userIds = redisRepository.getRoomMembers(workSpaceId, roomId); + Set userIds = redisRepository.getRoomMemberIds(workSpaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); sendUserList(workSpaceId, roomId); return new JoinResponseDto(roomId, meetRoom.getRoomName(), userInfoList, (long)userIds.size()); @@ -132,6 +132,7 @@ private JoinResponseDto joinWorkspaceRoom(String workSpaceId, Long userId, Long /** * 미팅룸에서 나갑니다. + * 웹소켓으로 변경된 사용자 목록을 전송합니다. * @param userId 사용자 ID */ @Transactional(readOnly = false) @@ -139,7 +140,7 @@ public void leaveRoom(Long userId) { String workspaceId = userService.getUserWorkSpaceId(userId); String meetRoomId = redisRepository.getUserRoomId(userId); if (meetRoomId == null) - return; + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "User is not in any room: " + userId); redisRepository.removeUserFromRoom(workspaceId, Long.parseLong(meetRoomId), userId.toString()); sendUserList(workspaceId, Long.parseLong(meetRoomId)); sendRoomList(workspaceId); @@ -163,11 +164,13 @@ public MeetingRoomListDto sendRoomList(String workSpaceId) { meetingRoomDto.setRoomName(meetRoom.getRoomName()); // 미팅룸에 참여한 사용자 수를 조회합니다. - Set roomMembers = redisRepository.getRoomMembers(workSpaceId, meetRoom.getRoomId()); + Set roomMembers = redisRepository.getRoomMemberIds(workSpaceId, meetRoom.getRoomId()); meetingRoomDto.setUserCount(roomMembers.size()); - // 빈 사용자 정보 리스트를 생성합니다. - meetingRoomDto.setUserInfoList(Collections.emptyList()); + // 사용자 정보 목록을 저장합니다. + List userInfoList = userService.getUserInfoList(roomMembers); + meetingRoomDto.setUserInfoList(userInfoList); + meetingRoomListDto.getMeetingRoomList().add(meetingRoomDto); } @@ -196,7 +199,7 @@ public MeetingRoomListDto sendRoomList() { * @param roomId 미팅룸 ID */ private void sendUserList(String workSpaceId, Long roomId) { - Set userIds = redisRepository.getRoomMembers(workSpaceId, roomId); + Set userIds = redisRepository.getRoomMemberIds(workSpaceId, roomId); List userInfoList = userService.getUserInfoList(userIds); simpMessagingTemplate.convertAndSend("/topic/meetingRoom/" + roomId + "/users", userInfoList); } diff --git a/src/main/java/capstone/relation/workspace/WorkSpace.java b/src/main/java/capstone/relation/workspace/WorkSpace.java index 05f4099..beff5c5 100644 --- a/src/main/java/capstone/relation/workspace/WorkSpace.java +++ b/src/main/java/capstone/relation/workspace/WorkSpace.java @@ -44,11 +44,7 @@ public void addUser(User user) { user.setWorkSpace(this); user.setInvitedWorkspaceId(""); } - - public void setId(String id) { - this.id = id; - } - + public void setName(String name) { this.name = name; } From 6c9b73b50c91f7b63d93d8fa564d6312a11dc81d Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 27 Jul 2024 19:08:21 +0900 Subject: [PATCH 17/23] =?UTF-8?q?Refactor:=20=EB=B9=8C=EB=8D=94=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/relation/meeting/dto/response/JoinResponseDto.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/capstone/relation/meeting/dto/response/JoinResponseDto.java b/src/main/java/capstone/relation/meeting/dto/response/JoinResponseDto.java index 0c78c71..a83523b 100644 --- a/src/main/java/capstone/relation/meeting/dto/response/JoinResponseDto.java +++ b/src/main/java/capstone/relation/meeting/dto/response/JoinResponseDto.java @@ -5,10 +5,12 @@ import capstone.relation.user.dto.UserInfoDto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; @Data @AllArgsConstructor +@Builder public class JoinResponseDto { @Schema(description = "회의실 ID", example = "123") private Long roomId; From 8cbf63974f1d4634077c4562b26a27c22013515b Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 27 Jul 2024 19:08:35 +0900 Subject: [PATCH 18/23] =?UTF-8?q?Refactor:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/relation/meeting/controller/MeetRoomController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java b/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java index 42adc67..f1e3dba 100644 --- a/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java +++ b/src/main/java/capstone/relation/meeting/controller/MeetRoomController.java @@ -35,7 +35,6 @@ public JoinResponseDto createRoom(@RequestBody CreateRoomDto createRoomDto) { @PostMapping("/join/{roomId}") @Operation(summary = "회의방 참여", description = "회의방에 참여합니다.") public JoinResponseDto joinRoom(@PathVariable Long roomId) { - //TODO:에러 발생 시켜보기. return meetRoomService.joinRoom(roomId); } From 6c426a9f9655747e160202cdd3c2bb16dc73b0d0 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 27 Jul 2024 19:09:37 +0900 Subject: [PATCH 19/23] Feat: Test yaml file Set --- src/test/resources/application-test.yml | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/resources/application-test.yml diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..fcff3dc --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,48 @@ +spring: + datasource: + url: jdbc:h2:mem:test + driverClassName: org.h2.Driver + username: sa + password: + + jpa: + hibernate: + ddl-auto: create + +oauth2: + jwt: + authorityKey: "roles" + bearerPrefix: "Bearer " + tokenSecret: "testSecretKeyShouldBeKeptSecretAndHasAtLeast32Bytes" + accessTokenHeader: "Authorization" + access-token-expire-day: 30 + refresh-token-expire-day: 30 + naver: + client-id: "test-client-id" + client-secret: "test-client-secret" + redirectUri: "http://localhost:8080/login/oauth2/code/naver" + + base-url: "https://nid.naver.com" + token-path: "/oauth2.0/token" + user-info-path: "/oauth2.0/profile" + authorizationUri: "https://nid.naver.com/oauth2.0/authorize" + tokenUri: "https://nid.naver.com/oauth2.0/token" + userInfoUri: "https://openapi.naver.com/v1/nid/me" + userNameAttributeName: response + + kakao: + name: Kakao + client-id: "test-client-id" + client-secret: "test-client-secret" + redirectUri: "http://localhost:8080/login/kakao/index.html" + authorizationUri: "https://kauth.kakao.com/oauth/authorize" + tokenUri: "https://kauth.kakao.com/oauth/token" + userInfoUri: "https://kapi.kakao.com/v2/user/me" + userNameAttributeName: id + +school: + info: + url: https://www.career.go.kr/cnet/openapi/getOpenApi + api: + key: + test-api-key \ No newline at end of file From 1302da1eadec565fa6f6d7671e3e33e573d618e6 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Sat, 27 Jul 2024 19:09:56 +0900 Subject: [PATCH 20/23] Feat: Delete not use Print --- .../security/WithMockCustomUserSecurityContextFactory.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java b/src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java index 95be24d..699087b 100644 --- a/src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java +++ b/src/test/java/capstone/relation/security/WithMockCustomUserSecurityContextFactory.java @@ -12,10 +12,8 @@ public class WithMockCustomUserSecurityContextFactory @Override public SecurityContext createSecurityContext(WithMockCustomUser customUser) { - System.out.println("customUser: " + customUser); SecurityContext context = SecurityContextHolder.createEmptyContext(); SecurityUser securityUser = SecurityUser.of(customUser.id(), customUser.role()); - System.out.println("securityUser: " + securityUser); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()); context.setAuthentication(auth); From 70c0f2046437e30a0692dc078d1d2f92b5ea05c0 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 31 Jul 2024 13:51:35 +0900 Subject: [PATCH 21/23] Feat: SignalingIntegrationTest --- .../signaling/service/SignalingService.java | 54 ++-- .../signaling/SignalingIntegrationTest.java | 244 ++++++++++++++++++ 2 files changed, 280 insertions(+), 18 deletions(-) create mode 100644 src/test/java/capstone/relation/websocket/signaling/SignalingIntegrationTest.java diff --git a/src/main/java/capstone/relation/websocket/signaling/service/SignalingService.java b/src/main/java/capstone/relation/websocket/signaling/service/SignalingService.java index 68a9474..e12cce8 100644 --- a/src/main/java/capstone/relation/websocket/signaling/service/SignalingService.java +++ b/src/main/java/capstone/relation/websocket/signaling/service/SignalingService.java @@ -13,40 +13,58 @@ @Service @RequiredArgsConstructor public class SignalingService { + private final UserService userService; private final SocketRegistry socketRegistry; - private final SimpMessagingTemplate simpMessagingTemplate; - private final UserService userService; - public void sendOffer(String roomId, SdpMessageDto sdpMessageDto, Long myId) { - System.out.println("sendOffer"); + /** + * Offer 메시지를 상대방에게 전송합니다.(목적지는 메시지를 확인해서 소켓 ID를 찾아서 전송합니다.) + * /user/queue/offer/{roomId} 로 전송합니다. + * @param roomId 참여중인 화상회의방 ID + * @param sdpMessageDto Offer 메시지 DTO + * @param senderId 보내는 사람 ID (내 Id) + */ + public void sendOffer(String roomId, SdpMessageDto sdpMessageDto, Long senderId) { String destId = sdpMessageDto.getUserId(); String socketId = socketRegistry.getSocketId(destId); - SdpResponseDto sdpResponseDto = new SdpResponseDto(); - sdpResponseDto.setUserInfo(userService.getUserInfo(myId)); - sdpResponseDto.setSessionDescription(sdpMessageDto.getSessionDescription()); - sdpResponseDto.setType(sdpMessageDto.getType()); + + SdpResponseDto sdpResponseDto = SdpResponseDto.builder() + .userInfo(userService.getUserInfo(senderId)) + .sessionDescription(sdpMessageDto.getSessionDescription()) + .type(sdpMessageDto.getType()) + .build(); simpMessagingTemplate.convertAndSendToUser(socketId, "/queue/offer/" + roomId, sdpResponseDto); } - public void sendAnswer(String roomId, SdpMessageDto sdpMessageDto, Long myId) { - System.out.println("sendAnswer"); + /** + * Answer 메시지를 상대방에게 전송합니다.(목적지는 메시지를 확인해서 소켓 ID를 찾아서 전송합니다.) + * /user/queue/answer/{roomId} 로 전송합니다. + * @param roomId 참여중인 화상회의방 ID + * @param sdpMessageDto Answer 메시지 DTO + * @param senderId 보내는 사람 ID (내 Id) + */ + public void sendAnswer(String roomId, SdpMessageDto sdpMessageDto, Long senderId) { String socketId = socketRegistry.getSocketId(sdpMessageDto.getUserId()); - SdpResponseDto sdpResponseDto = new SdpResponseDto(); - sdpResponseDto.setUserInfo(userService.getUserInfo(myId)); - sdpResponseDto.setSessionDescription(sdpMessageDto.getSessionDescription()); - sdpResponseDto.setType(sdpMessageDto.getType()); + SdpResponseDto sdpResponseDto = SdpResponseDto.builder() + .userInfo(userService.getUserInfo(senderId)) + .sessionDescription(sdpMessageDto.getSessionDescription()) + .type(sdpMessageDto.getType()) + .build(); simpMessagingTemplate.convertAndSendToUser(socketId, "/queue/answer/" + roomId, sdpResponseDto); } - public void sendIce(String roomId, IceDto iceDto, Long myId) { - System.out.println("sendIce"); + /** + * Ice 메시지를 상대방에게 전송합니다.(목적지는 메시지를 확인해서 소켓 ID를 찾아서 전송합니다.) + * @param roomId 참여중인 화상회의방 ID + * @param iceDto Ice 메시지 DTO + * @param senderId 보내는 사람 ID (내 Id) + */ + public void sendIce(String roomId, IceDto iceDto, Long senderId) { String destId = iceDto.getUserId(); String socketId = socketRegistry.getSocketId(destId); - iceDto.setUserId(myId.toString()); // 보내는 사람 ID로 갈아 껴줌. + iceDto.setUserId(senderId.toString()); // 보내는 사람 ID로 갈아 껴줌. iceDto.setType(iceDto.getType()); simpMessagingTemplate.convertAndSendToUser(socketId, "/queue/ice/" + roomId, iceDto); } - } diff --git a/src/test/java/capstone/relation/websocket/signaling/SignalingIntegrationTest.java b/src/test/java/capstone/relation/websocket/signaling/SignalingIntegrationTest.java new file mode 100644 index 0000000..8a3ea3c --- /dev/null +++ b/src/test/java/capstone/relation/websocket/signaling/SignalingIntegrationTest.java @@ -0,0 +1,244 @@ +package capstone.relation.websocket.signaling; + +import static capstone.relation.user.domain.Role.*; +import static capstone.relation.websocket.signaling.dto.SignalMessageType.*; +import static org.assertj.core.api.AssertionsForClassTypes.*; + +import java.lang.reflect.Type; +import java.util.Date; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.messaging.converter.MappingJackson2MessageConverter; +import org.springframework.messaging.simp.stomp.StompFrameHandler; +import org.springframework.messaging.simp.stomp.StompHeaders; +import org.springframework.messaging.simp.stomp.StompSession; +import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.web.socket.WebSocketHttpHeaders; +import org.springframework.web.socket.client.standard.StandardWebSocketClient; +import org.springframework.web.socket.messaging.WebSocketStompClient; + +import capstone.relation.api.auth.jwt.TokenProvider; +import capstone.relation.user.domain.User; +import capstone.relation.user.repository.UserRepository; +import capstone.relation.websocket.signaling.dto.IceDto; +import capstone.relation.websocket.signaling.dto.SdpDto; +import capstone.relation.websocket.signaling.dto.SdpMessageDto; +import capstone.relation.websocket.signaling.dto.SdpResponseDto; +import capstone.relation.workspace.WorkSpace; +import capstone.relation.workspace.repository.WorkSpaceRepository; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") //테스트 프로필 활성화. +@ExtendWith(SpringExtension.class) //단위 테스트에 공통적으로 사용할 확장 기능을 선언 +public class SignalingIntegrationTest { + @LocalServerPort + private int port; + + private String WEBSOCKET_URI; + private String WEBSOCKET_TOPIC; + + private WebSocketStompClient stompClient; + private User sender; + private User receiver; + + private Date accessTokenExpiredDate; + + @Autowired + private TokenProvider tokenProvider; + + @Autowired + private WorkSpaceRepository workSpaceRepository; + + @Autowired + private UserRepository userRepository; + + @BeforeEach + public void setup() { + MockitoAnnotations.openMocks(this); + this.WEBSOCKET_URI = "ws://localhost:" + port + "/ws-chat"; + this.WEBSOCKET_TOPIC = "/app"; + this.stompClient = new WebSocketStompClient(new StandardWebSocketClient()); + this.stompClient.setMessageConverter(new MappingJackson2MessageConverter()); + sender = User.builder() + .email("wnddms12345@gmail.com") + .profileImage("profileImage") + .provider("local") + .userName("senderName") + .role(USER) + .build(); + WorkSpace workSpace = new WorkSpace(); + workSpace.setName("workSpaceName"); + workSpaceRepository.save(workSpace); + sender.setWorkSpace(workSpace); + userRepository.save(sender); + + receiver = User.builder() + .email("wnddms12345@gmail.com") + .profileImage("profileImage") + .provider("local") + .userName("receiverName") + .role(USER) + .build(); + receiver.setWorkSpace(workSpace); + userRepository.save(receiver); + + accessTokenExpiredDate = new Date(new Date().getTime() + 10000000L); + } + + private StompSession connectWebSocket(User user) throws + InterruptedException, + ExecutionException { + + System.out.println("유저 ID: " + user.getId()); + String token = tokenProvider.generateAccessToken(user, accessTokenExpiredDate); + System.out.println("임시 엑세스 토큰 생성: " + token); + + //연결 시 사용하는 JWT 설정 + WebSocketHttpHeaders headers = new WebSocketHttpHeaders(); + headers.add("Authorization", "Bearer " + token); + //STOMP 사용시 사용하는 JWT 설정 + StompHeaders stompHeaders = new StompHeaders(); + stompHeaders.add("Authorization", "Bearer " + token); + + //STOMP 연결 + StompSession session = stompClient.connectAsync(WEBSOCKET_URI, headers, stompHeaders, + new StompSessionHandlerAdapter() { + @Override + public void afterConnected(StompSession session, StompHeaders connectedHeaders) { + System.out.println("STOMP 연결 성공"); + } + }).get(); + + return session; + } + + @Test + @DisplayName("소켓으로 연결된 유저에게 ice 메시지 전송할 수 있다.") + public void testIce() throws InterruptedException, ExecutionException, TimeoutException { + BlockingQueue messageQueue = new LinkedBlockingQueue<>(); + + IceDto iceDto = new IceDto(); + iceDto.setType(ScreenShare); + iceDto.setCandidate("candidate"); + iceDto.setSdpMid("sdpMid"); + iceDto.setSdpMLineIndex("sdpMLineIndex"); + iceDto.setUserId(receiver.getId().toString()); + //유저 2명 연결 1이 송신 2가 수신. + StompSession senderSession = connectWebSocket(sender); + StompSession receiverSession = connectWebSocket(receiver); + receiverSession.subscribe("/user/queue/ice/123", new StompFrameHandler() { + @Override + public Type getPayloadType(StompHeaders headers) { + System.out.println("구독 시작: /user/queue/ice/123"); + return IceDto.class; + } + + @Override + public void handleFrame(StompHeaders headers, Object payload) { + System.out.println("ice 응답값 : " + payload); + messageQueue.offer((IceDto)payload); + } + }); + StompHeaders stompHeaders = new StompHeaders(); + stompHeaders.setDestination(WEBSOCKET_TOPIC + "/ice/123"); + + senderSession.send(stompHeaders, iceDto); + + IceDto receivedMessage = messageQueue.poll(5, TimeUnit.SECONDS); + assertThat(receivedMessage).isNotNull(); + assertThat(receivedMessage.getUserId()).isEqualTo(sender.getId().toString()); + } + + @Test + @DisplayName("offer 테스트") + public void testOffer() throws InterruptedException, ExecutionException, TimeoutException { + BlockingQueue messageQueue = new LinkedBlockingQueue<>(); + + SdpMessageDto sdpMessageDto = new SdpMessageDto(); + sdpMessageDto.setUserId(receiver.getId().toString()); + sdpMessageDto.setType(ScreenShare); + SdpDto sdpDto = new SdpDto(); + sdpDto.setSdp("sdp"); + sdpDto.setType("offer"); + sdpMessageDto.setSessionDescription(sdpDto); + + //유저 2명 연결 1이 송신 2가 수신. + StompSession senderSession = connectWebSocket(sender); + StompSession receiverSession = connectWebSocket(receiver); + receiverSession.subscribe("/user/queue/offer/123", new StompFrameHandler() { + @Override + public Type getPayloadType(StompHeaders headers) { + System.out.println("구독 시작: /user/queue/offer/123"); + return SdpResponseDto.class; + } + + @Override + public void handleFrame(StompHeaders headers, Object payload) { + System.out.println("offer 응답값 : " + payload); + messageQueue.offer((SdpResponseDto)payload); + } + }); + + StompHeaders stompHeaders = new StompHeaders(); + stompHeaders.setDestination(WEBSOCKET_TOPIC + "/offer/123"); + + senderSession.send(stompHeaders, sdpMessageDto); + + SdpResponseDto receivedMessage = messageQueue.poll(5, TimeUnit.SECONDS); + assertThat(receivedMessage).isNotNull(); + assertThat(receivedMessage.getUserInfo().getUserId()).isEqualTo(sender.getId()); + } + + @Test + @DisplayName("소켓으로 연결된 유저에게 answer 메시지 전송할 수 있다.") + public void answerTest() throws InterruptedException, ExecutionException, TimeoutException { + BlockingQueue messageQueue = new LinkedBlockingQueue<>(); + + SdpMessageDto sdpMessageDto = new SdpMessageDto(); + SdpDto sdpDto = new SdpDto(); + sdpDto.setSdp("sdp"); + sdpDto.setType("answer"); + sdpMessageDto.setSessionDescription(sdpDto); + sdpMessageDto.setUserId(receiver.getId().toString()); + sdpMessageDto.setType(ScreenShare); + + //유저 2명 연결 1이 송신 2가 수신. + StompSession senderSession = connectWebSocket(sender); + StompSession receiverSession = connectWebSocket(receiver); + receiverSession.subscribe("/user/queue/answer/123", new StompFrameHandler() { + @Override + public Type getPayloadType(StompHeaders headers) { + System.out.println("구독 시작: /user/queue/answer/123"); + return SdpResponseDto.class; + } + + @Override + public void handleFrame(StompHeaders headers, Object payload) { + System.out.println("ice 응답값 : " + payload); + messageQueue.offer((SdpResponseDto)payload); + } + }); + StompHeaders stompHeaders = new StompHeaders(); + stompHeaders.setDestination(WEBSOCKET_TOPIC + "/answer/123"); + + senderSession.send(stompHeaders, sdpMessageDto); + + SdpResponseDto receivedMessage = messageQueue.poll(5, TimeUnit.SECONDS); + assertThat(receivedMessage).isNotNull(); + // assertThat(receivedMessage.getUserId()).isEqualTo(sender.getId().toString()); + } +} From 680663a8778280f10a447935d2fde54a0504546a Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 31 Jul 2024 13:54:10 +0900 Subject: [PATCH 22/23] Refactor: For Test --- .../capstone/relation/api/auth/jwt/TokenProvider.java | 3 +-- src/main/java/capstone/relation/user/UserService.java | 5 +++++ .../java/capstone/relation/user/dto/UserInfoDto.java | 6 ++---- .../relation/websocket/WebSocketEventListener.java | 6 +----- .../{controller => }/SignalingController.java | 3 +-- .../{controller => }/SignalingDocumentController.java | 2 +- .../websocket/signaling/dto/SdpResponseDto.java | 10 ++++++++++ 7 files changed, 21 insertions(+), 14 deletions(-) rename src/main/java/capstone/relation/websocket/signaling/{controller => }/SignalingController.java (94%) rename src/main/java/capstone/relation/websocket/signaling/{controller => }/SignalingDocumentController.java (99%) diff --git a/src/main/java/capstone/relation/api/auth/jwt/TokenProvider.java b/src/main/java/capstone/relation/api/auth/jwt/TokenProvider.java index 7b62a81..411fb64 100644 --- a/src/main/java/capstone/relation/api/auth/jwt/TokenProvider.java +++ b/src/main/java/capstone/relation/api/auth/jwt/TokenProvider.java @@ -122,8 +122,7 @@ public String getWorkSpaceIdByInviteCode(String inviteCode) { } } - private String generateAccessToken(User user, Date accessTokenExpiredDate) { - + public String generateAccessToken(User user, Date accessTokenExpiredDate) { return Jwts.builder() .setSubject(String.valueOf(user.getId())) .claim(jwtProperties.getAuthorityKey(), user.getRole().toString()) diff --git a/src/main/java/capstone/relation/user/UserService.java b/src/main/java/capstone/relation/user/UserService.java index 653664a..d1f4570 100644 --- a/src/main/java/capstone/relation/user/UserService.java +++ b/src/main/java/capstone/relation/user/UserService.java @@ -83,6 +83,11 @@ public String getUserWorkSpaceId(Long userId) { .getId(); } + /** + * 사용자 Id 목록을 받아서 사용자 정보 목록을 반환합니다. + * @param userIds 사용자 Id 목록 + * @return 사용자 정보 목록 + */ public List getUserInfoList(Set userIds) { List userInfoList = new ArrayList<>(); for (String id : userIds) { diff --git a/src/main/java/capstone/relation/user/dto/UserInfoDto.java b/src/main/java/capstone/relation/user/dto/UserInfoDto.java index 6b0c184..11e52d7 100644 --- a/src/main/java/capstone/relation/user/dto/UserInfoDto.java +++ b/src/main/java/capstone/relation/user/dto/UserInfoDto.java @@ -3,12 +3,10 @@ import capstone.relation.user.domain.User; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; +import lombok.Data; import lombok.NoArgsConstructor; -import lombok.Setter; -@Getter -@Setter +@Data @NoArgsConstructor public class UserInfoDto { @Schema(description = "사용자 ID", example = "1234567890") diff --git a/src/main/java/capstone/relation/websocket/WebSocketEventListener.java b/src/main/java/capstone/relation/websocket/WebSocketEventListener.java index 1c54f00..cbf119e 100644 --- a/src/main/java/capstone/relation/websocket/WebSocketEventListener.java +++ b/src/main/java/capstone/relation/websocket/WebSocketEventListener.java @@ -18,19 +18,15 @@ public class WebSocketEventListener { @EventListener public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage()); - // String sessionId = headerAccessor.getSessionId(); String socketId = headerAccessor.getUser().getName(); Long userId = (Long)headerAccessor.getSessionAttributes().get("userId"); - String workSpaceId = (String)headerAccessor.getSessionAttributes().get("workSpaceId"); System.out.println("User Disconnected : " + userId); - System.out.println("socketId : " + socketId); if (userId == null) { return; } meetRoomService.leaveRoom(userId); System.out.println("register SocketId" + socketRegistry.getSocketId(userId.toString())); - if (socketId == socketRegistry.getSocketId(userId.toString())) { + if (socketId == socketRegistry.getSocketId(userId.toString())) socketRegistry.unregisterSession(userId.toString()); - } } } diff --git a/src/main/java/capstone/relation/websocket/signaling/controller/SignalingController.java b/src/main/java/capstone/relation/websocket/signaling/SignalingController.java similarity index 94% rename from src/main/java/capstone/relation/websocket/signaling/controller/SignalingController.java rename to src/main/java/capstone/relation/websocket/signaling/SignalingController.java index 3d0b987..9d3f1a1 100644 --- a/src/main/java/capstone/relation/websocket/signaling/controller/SignalingController.java +++ b/src/main/java/capstone/relation/websocket/signaling/SignalingController.java @@ -1,4 +1,4 @@ -package capstone.relation.websocket.signaling.controller; +package capstone.relation.websocket.signaling; import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; @@ -18,7 +18,6 @@ public class SignalingController { @MessageMapping("/ice/{roomId}") public void ice(@DestinationVariable String roomId, IceDto iceDto, SimpMessageHeaderAccessor headerAccessor) { - System.out.println("ice"); Long userId = (Long)headerAccessor.getSessionAttributes().get("userId"); signalingService.sendIce(roomId, iceDto, userId); } diff --git a/src/main/java/capstone/relation/websocket/signaling/controller/SignalingDocumentController.java b/src/main/java/capstone/relation/websocket/signaling/SignalingDocumentController.java similarity index 99% rename from src/main/java/capstone/relation/websocket/signaling/controller/SignalingDocumentController.java rename to src/main/java/capstone/relation/websocket/signaling/SignalingDocumentController.java index d09626e..b929866 100644 --- a/src/main/java/capstone/relation/websocket/signaling/controller/SignalingDocumentController.java +++ b/src/main/java/capstone/relation/websocket/signaling/SignalingDocumentController.java @@ -1,4 +1,4 @@ -package capstone.relation.websocket.signaling.controller; +package capstone.relation.websocket.signaling; import java.util.List; diff --git a/src/main/java/capstone/relation/websocket/signaling/dto/SdpResponseDto.java b/src/main/java/capstone/relation/websocket/signaling/dto/SdpResponseDto.java index 915e8a4..1df17e0 100644 --- a/src/main/java/capstone/relation/websocket/signaling/dto/SdpResponseDto.java +++ b/src/main/java/capstone/relation/websocket/signaling/dto/SdpResponseDto.java @@ -2,9 +2,12 @@ import capstone.relation.user.dto.UserInfoDto; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@NoArgsConstructor @Schema(description = "offer 또는 answer를 받을 때 사용하는 DTO") public class SdpResponseDto { @Schema(description = "메시지를 보낸 사용자 정보(즉 자신이 아닌 통신하고 있는 유저 id)") @@ -15,4 +18,11 @@ public class SdpResponseDto { @Schema(description = "메시지 타입 \n" + "ScreenShare | Video", example = "ScreenShare") private SignalMessageType type; + + @Builder + public SdpResponseDto(UserInfoDto userInfo, SdpDto sessionDescription, SignalMessageType type) { + this.userInfo = userInfo; + this.sessionDescription = sessionDescription; + this.type = type; + } } From f38b5484acb57cd55e922d9b8dce7a6280b272f8 Mon Sep 17 00:00:00 2001 From: hawardShin Date: Wed, 31 Jul 2024 14:03:53 +0900 Subject: [PATCH 23/23] Chore: CheckStyle --- src/main/java/capstone/relation/workspace/WorkSpace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/relation/workspace/WorkSpace.java b/src/main/java/capstone/relation/workspace/WorkSpace.java index beff5c5..c23842b 100644 --- a/src/main/java/capstone/relation/workspace/WorkSpace.java +++ b/src/main/java/capstone/relation/workspace/WorkSpace.java @@ -44,7 +44,7 @@ public void addUser(User user) { user.setWorkSpace(this); user.setInvitedWorkspaceId(""); } - + public void setName(String name) { this.name = name; }