Skip to content

Commit

Permalink
Merge pull request #307 from kSideProject/dev
Browse files Browse the repository at this point in the history
dev 브랜치 main으로 병합
  • Loading branch information
jihyun-j authored Nov 28, 2024
2 parents 48a4b02 + 5341d22 commit 02a197d
Show file tree
Hide file tree
Showing 58 changed files with 912 additions and 483 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package kpring.chat.chatroom.api.v1
import kpring.chat.chatroom.service.ChatRoomService
import kpring.core.chat.chatroom.dto.request.CreateChatRoomRequest
import kpring.core.chat.chatroom.dto.request.ExpelChatRoomRequest
import kpring.core.chat.chatroom.dto.request.TransferChatRoomOwnerRequest
import kpring.core.chat.chatroom.dto.response.MessageStatus
import kpring.core.global.dto.response.ApiResponse
import lombok.RequiredArgsConstructor
Expand Down Expand Up @@ -67,4 +68,14 @@ class WebSocketChatRoomController(
ApiResponse(status = MessageStatus.DISCONNECT.code, MessageStatus.DISCONNECT.description, data = null),
)
}

@MessageMapping("/chatroom/transfer")
fun transferChatRoomOwnerShip(
@Payload transferChatRoomOwnerRequest: TransferChatRoomOwnerRequest,
principal: Principal,
) {
val userId = principal.name
val result = chatRoomService.transferChatRoomOwnerShip(transferChatRoomOwnerRequest, userId)
simpMessagingTemplate.convertAndSend("/topic/chatroom/${result.chatRoomId}", ApiResponse(status = 200, data = result.chatResponse))
}
}
6 changes: 5 additions & 1 deletion chat/src/main/kotlin/kpring/chat/chatroom/model/ChatRoom.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.springframework.data.mongodb.core.mapping.Document
@Document(collection = "chatrooms")
class ChatRoom(
@Id val id: String? = null,
val ownerId: String? = null,
var ownerId: String? = null,
val members: MutableSet<String> = mutableSetOf(),
) : BaseTime() {
fun getUsers(): Set<String> {
Expand All @@ -27,4 +27,8 @@ class ChatRoom(
fun removeUser(userId: String) {
members.remove(userId)
}

fun transferOwnership(newOwnerId: String) {
ownerId = newOwnerId
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import kpring.core.chat.chat.dto.response.EventType
import kpring.core.chat.chat.dto.response.InvitationResponse
import kpring.core.chat.chatroom.dto.request.CreateChatRoomRequest
import kpring.core.chat.chatroom.dto.request.ExpelChatRoomRequest
import kpring.core.chat.chatroom.dto.request.TransferChatRoomOwnerRequest
import kpring.core.chat.model.ChatType
import kpring.core.chat.model.MessageType
import org.springframework.stereotype.Service
Expand All @@ -32,7 +33,7 @@ class ChatRoomService(
val chatRoom = ChatRoom(ownerId = userId, members = mutableSetOf(userId))
chatRoom.addUsers(request.users)
val saved = chatRoomRepository.save(chatRoom)
return createChatRoomMessage(saved.id!!, "방이 생성되었습니다.", EventType.CREATED)
return createChatRoomMessage(saved.id!!, "방이 생성되었습니다.", EventType.SYSTEM)
}

fun exitChatRoom(
Expand All @@ -43,7 +44,7 @@ class ChatRoomService(
val chatRoom: ChatRoom = getChatRoom(chatRoomId)
chatRoom.removeUser(userId)
chatRoomRepository.save(chatRoom)
return createChatRoomMessage(chatRoom.id!!, "${userId}님이 방에서 나갔습니다.", EventType.EXIT) // TODO : 닉네임으로 변경
return createChatRoomMessage(chatRoom.id!!, "${userId}님이 방에서 나갔습니다.", EventType.SYSTEM) // TODO : 닉네임으로 변경
}

fun getChatRoomInvitation(
Expand All @@ -68,7 +69,7 @@ class ChatRoomService(
val chatRoom = getChatRoom(invitationInfo.chatRoomId)
chatRoom.addUser(userId)
chatRoomRepository.save(chatRoom)
return createChatRoomMessage(chatRoom.id!!, "${userId}님이 방에 들어왔습니다.", EventType.ENTER) // TODO : 닉네임으로 변경
return createChatRoomMessage(chatRoom.id!!, "${userId}님이 방에 들어왔습니다.", EventType.SYSTEM) // TODO : 닉네임으로 변경
}

fun expelFromChatRoom(
Expand All @@ -79,7 +80,19 @@ class ChatRoomService(
accessVerifier.verifyChatRoomOwner(expelChatRoomRequest.chatRoomId, userId)
chatRoom.removeUser(expelChatRoomRequest.expelUserId)
chatRoomRepository.save(chatRoom)
return createChatRoomMessage(chatRoom.id!!, "${expelChatRoomRequest.expelUserId}님이 방에서 내보내졌습니다.", EventType.EXPEL) // TODO : 닉네임으로 변경
return createChatRoomMessage(chatRoom.id!!, "${expelChatRoomRequest.expelUserId}님이 방에서 내보내졌습니다.", EventType.SYSTEM) // TODO : 닉네임으로 변경
}

fun transferChatRoomOwnerShip(
transferChatRoomOwnerRequest: TransferChatRoomOwnerRequest,
userId: String,
): ChatWrapper {
val chatRoom = getChatRoom(transferChatRoomOwnerRequest.chatRoomId)
accessVerifier.verifyChatRoomOwner(transferChatRoomOwnerRequest.chatRoomId, userId)
accessVerifier.verifyChatRoomOwner(transferChatRoomOwnerRequest.chatRoomId, transferChatRoomOwnerRequest.newOwnerId)
chatRoom.transferOwnership(transferChatRoomOwnerRequest.newOwnerId)
chatRoomRepository.save(chatRoom)
return createChatRoomMessage(chatRoom.id!!, "${transferChatRoomOwnerRequest.newOwnerId}님이 새 방장으로 임명되었습니다.", EventType.CHAT)
}

private fun verifyInvitationExistence(invitationInfo: InvitationInfo) {
Expand Down
55 changes: 50 additions & 5 deletions chat/src/test/kotlin/kpring/chat/chatroom/ChatRoomServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import kpring.chat.global.util.AccessVerifier
import kpring.core.chat.chat.dto.response.EventType
import kpring.core.chat.chatroom.dto.request.CreateChatRoomRequest
import kpring.core.chat.chatroom.dto.request.ExpelChatRoomRequest
import kpring.core.chat.chatroom.dto.request.TransferChatRoomOwnerRequest
import kpring.core.chat.model.ChatType
import kpring.core.chat.model.MessageType
import java.util.*
Expand Down Expand Up @@ -53,7 +54,7 @@ class ChatRoomServiceTest : FunSpec({
id = "chat_id",
userId = "",
chatType = ChatType.ROOM,
eventType = EventType.CREATED,
eventType = EventType.SYSTEM,
contextId = chatRoom.id!!,
content = "방이 생성되었습니다.",
)
Expand Down Expand Up @@ -90,7 +91,7 @@ class ChatRoomServiceTest : FunSpec({
id = "chat_id",
userId = "",
chatType = ChatType.ROOM,
eventType = EventType.EXIT,
eventType = EventType.SYSTEM,
contextId = chatRoomId,
content = "${userId}님이 방에서 나갔습니다.",
)
Expand Down Expand Up @@ -180,7 +181,7 @@ class ChatRoomServiceTest : FunSpec({
id = "chat_id",
userId = "",
chatType = ChatType.ROOM,
eventType = EventType.ENTER,
eventType = EventType.SYSTEM,
contextId = chatRoomId,
content = "${userId}님이 방에 들어왔습니다.",
)
Expand Down Expand Up @@ -236,7 +237,7 @@ class ChatRoomServiceTest : FunSpec({
id = "chat_id",
userId = "",
chatType = ChatType.ROOM,
eventType = EventType.EXPEL,
eventType = EventType.SYSTEM,
contextId = chatRoomId,
content = "${ownerId}님이 방에서 내보내졌습니다.",
)
Expand Down Expand Up @@ -270,12 +271,56 @@ class ChatRoomServiceTest : FunSpec({
val request = ExpelChatRoomRequest(chatRoomId, expelUserId)

every { chatRoomRepository.findById(any()) } returns Optional.of(chatRoom)
every { accessVerifier.verifyChatRoomOwner(expelUserId, userId) } throws GlobalException(ErrorCode.FORBIDDEN_CHATROOM)
every {
accessVerifier.verifyChatRoomOwner(
expelUserId,
userId,
)
} throws GlobalException(ErrorCode.FORBIDDEN_CHATROOM)

// When & Then
shouldThrow<GlobalException>({
chatRoomService.expelFromChatRoom(request, userId)
})
}

test("채팅방 소유권을 성공적으로 위임할 수 있어야 한다") {
// Given
val chatRoomId = "test_chat_room_id"
val currentOwnerId = "current_owner_id"
val newOwnerId = "new_owner_id"

val chatRoom =
ChatRoom(
id = chatRoomId,
ownerId = currentOwnerId,
members = mutableSetOf(currentOwnerId, newOwnerId),
)

val expectedChat =
Chat(
id = "chat_id",
userId = "",
chatType = ChatType.ROOM,
eventType = EventType.SYSTEM,
contextId = chatRoomId,
content = "${newOwnerId}님이 새 방장으로 임명되었습니다.",
)

val request = TransferChatRoomOwnerRequest(chatRoomId, newOwnerId)

every { chatRoomRepository.findById(any()) } returns Optional.of(chatRoom)
every { accessVerifier.verifyChatRoomOwner(any(), any()) } just runs
every { chatRoomRepository.save(any()) } returns chatRoom
every { chatRepository.save(any()) } returns expectedChat

// When
val result = chatRoomService.transferChatRoomOwnerShip(request, currentOwnerId)

// Then
verify { accessVerifier.verifyChatRoomOwner(any(), any()) }
verify { chatRoomRepository.save(any()) }
result.chatResponse.content shouldBe "${newOwnerId}님이 새 방장으로 임명되었습니다."
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class ChatRoomControllerTest(
Chat(
userId = "",
chatType = ChatType.ROOM,
eventType = EventType.ENTER,
eventType = EventType.SYSTEM,
contextId = chatRoomId,
content = content,
)
Expand All @@ -135,7 +135,7 @@ class ChatRoomControllerTest(
id = "1",
sender = userId,
messageType = MessageType.CHAT,
eventType = EventType.ENTER,
eventType = EventType.SYSTEM,
isEdited = chat.isEdited(),
sentAt = chat.updatedAt.toString(),
content = chat.content,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package kpring.core.chat.chat.dto.response

enum class EventType(val type: String) {
ENTER("ENTER"),
INVITATION("INVITATION"),
CREATED("CREATED"),
CHAT("CHAT"),
EXIT("EXIT"),
EXPEL("EXPEL"),
SYSTEM("SYSTEM"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package kpring.core.chat.chatroom.dto.request

import jakarta.validation.constraints.NotNull

data class TransferChatRoomOwnerRequest(
@field:NotNull
val newOwnerId: String,
@field:NotNull
val chatRoomId: String,
)
2 changes: 2 additions & 0 deletions front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"axios": "^1.7.2",
"boring-avatars": "^1.11.2",
"embla-carousel-autoplay": "^8.2.1",
"embla-carousel-react": "^8.2.1",
"express": "^4.19.2",
Expand All @@ -25,6 +26,7 @@
"phaser-animated-tiles-phaser3.5": "^2.0.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-router": "^6.23.0",
"react-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
Expand Down
8 changes: 4 additions & 4 deletions front/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Navigate, Route, Routes } from "react-router-dom";
import "./App.css";
import AuthLayout from "./components/Auth/AuthLayout";
import PrivateRoute from "./components/Auth/PrivateRoute";
import Layout from "./components/Layout/Layout";
import AuthLayout from "./components/auth/AuthLayout";
import PrivateRoute from "./components/auth/PrivateRoute";
import Layout from "./components/layout/Layout";
import Home from "./pages/Home";
import Join from "./pages/Join";
import Login from "./pages/Login";
import ServerMapWithTheme from "./components/Server/ServerMapWithTheme";
import ServerMapWithTheme from "./components/server/ServerMapWithTheme";

function App() {
return (
Expand Down
1 change: 1 addition & 0 deletions front/src/api/axiosInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ axios 인터셉터로 위의 로직대로 코드 작성 하다가 요청 인터
token.ts의 두 함수(validateAccessToken, refreshAccessToken)에서 요청을 보내는 부분이 인터셉터에 의해 다시 가로채지고 있는 것으로 추정
이를 해결하기 위한 방법으로 아래의 인스턴스를 별도 만들어서 해결함
*/

import axios from "axios";

const axiosInstance = axios.create({
Expand Down
48 changes: 48 additions & 0 deletions front/src/api/requestFriend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import axiosInstance from "./axiosInstance";

type FriendsType = {
friendId: number;
username: string;
email: string;
imagePath: string;
};

type RequestFriendType = {
userId: string;
friends: FriendsType[];
};

export const requestFriend = async (token: string | null) => {
const url = `${process.env.REACT_APP_BASE_URL}/user/api/v1/user/4/friend/2`;

try {
const response = await axiosInstance({
method: "post",
url,
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(response.data);
return response.data as RequestFriendType;
} catch (error) {
console.log(error);
}
};

export const viewFriendRequest = async (token: string) => {
const url = `${process.env.REACT_APP_BASE_URL}/user/api/v1/user/2/requests`;
try {
const response = await axiosInstance({
method: "get",
url,
headers: {
Authorization: `Bearer ${token}`,
},
});

return response.data;
} catch (error) {
console.log(error);
}
};
15 changes: 5 additions & 10 deletions front/src/components/Auth/LoginBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ function LoginBox() {
"
border="1px solid #e4d4e7"
padding="20px"
onSubmit={clickSubmitHandler}
>
onSubmit={clickSubmitHandler}>
<h2 className="text-center text-2xl font-bold text-primary mt-[5px] mb-[10px]">
디코타운에 어서오세요!
</h2>
Expand Down Expand Up @@ -181,8 +180,7 @@ function LoginBox() {
type="submit"
variant="contained"
startIcon={<LoginIcon />}
sx={{ width: "90%" }}
>
sx={{ width: "90%" }}>
로그인
</Button>

Expand All @@ -191,22 +189,19 @@ function LoginBox() {
color="secondary"
startIcon={<PersonAddAlt1Icon />}
sx={{ mt: "20px", width: "90%", mb: "20px" }}
onClick={() => navigate("/join")}
>
onClick={() => navigate("/join")}>
회원가입
</Button>
</div>
</Box>
<Snackbar
open={open}
autoHideDuration={6000}
onClose={clickCloseHandler}
>
onClose={clickCloseHandler}>
<Alert
onClose={clickCloseHandler}
severity={alertInfo.severity}
sx={{ width: "100%" }}
>
sx={{ width: "100%" }}>
{alertInfo.message}
</Alert>
</Snackbar>
Expand Down
Loading

0 comments on commit 02a197d

Please sign in to comment.