diff --git a/src/mocks/mockAPI.ts b/src/mocks/mockAPI.ts index 354ecee4..fdd8b1d6 100644 --- a/src/mocks/mockAPI.ts +++ b/src/mocks/mockAPI.ts @@ -7,11 +7,13 @@ import { TASK_DUMMY, TASK_FILE_DUMMY, TASK_USER_DUMMY, + TEAM_USER_DUMMY, USER_DUMMY, } from '@mocks/mockData'; import type { Role } from '@/types/RoleType'; import type { User } from '@/types/UserType'; +import type { Team } from '@/types/TeamType'; import type { Project } from '@/types/ProjectType'; import type { ProjectStatus, ProjectStatusForm } from '@/types/ProjectStatusType'; import type { Task, TaskUpdateForm } from '@/types/TaskType'; @@ -31,20 +33,53 @@ export function findUser(userId: User['userId']) { return USER_DUMMY.find((user) => user.userId === userId); } -/* ============= 프로젝트 유저(Project User) 관련 처리 ============= */ +/* ============= 팀에 연결된 유저(Team User) 관련 처리 ============= */ -// 프로젝트 유저 조회 +// 팀과 연결된 유저 조회 +export function findTeamUser(teamId: Team['teamId'], userId: User['userId']) { + return TEAM_USER_DUMMY.find((teamUser) => teamUser.teamId === teamId && teamUser.userId === userId); +} + +/* ========= 프로젝트에 연결된 유저(Project User) 관련 처리 ========= */ + +// 프로젝트와 연결된 유저 조회 export function findProjectUser(projectId: Project['projectId'], userId: User['userId']) { return PROJECT_USER_DUMMY.find((projectUser) => projectUser.projectId === projectId && projectUser.userId === userId); } -/* ================= 프로젝트(Project ) 관련 처리 ================= */ +// 프로젝트의 연결된 모든 유저 조회 +export function findAllProjectUser(projectId: Project['projectId']) { + return PROJECT_USER_DUMMY.filter((projectUser) => projectUser.projectId === projectId); +} + +// 프로젝트와 연결된 모든 유저 삭제 +export function deleteAllProjectUser(projectId: Project['projectId']) { + const filteredProjectUsers = PROJECT_USER_DUMMY.filter((projectUser) => projectUser.projectId !== projectId); + if (PROJECT_USER_DUMMY.length !== filteredProjectUsers.length) { + PROJECT_USER_DUMMY.length = 0; + PROJECT_USER_DUMMY.push(...filteredProjectUsers); + } +} + +/* ================= 프로젝트(Project) 관련 처리 ================= */ // 프로젝트 조회 export function findProject(projectId: Project['projectId']) { return PROJECT_DUMMY.find((project) => project.projectId === projectId); } +// 특정 팀의 모든 프로젝트 조회 +export function findAllProject(teamId: Team['teamId']) { + return PROJECT_DUMMY.filter((project) => project.teamId === teamId); +} + +// 프로젝트 삭제 +export function deleteProject(projectId: Project['projectId']) { + const projectIndex = PROJECT_DUMMY.findIndex((project) => project.projectId === projectId); + if (projectIndex === -1) throw new Error('프로젝트를 찾을 수 없습니다.'); + PROJECT_DUMMY.splice(projectIndex, 1); +} + /* ================ 프로젝트 상태(Status) 관련 처리 ================ */ // 프로젝트 상태 정보 생성 @@ -80,6 +115,15 @@ export function deleteProjectStatus(statusId: ProjectStatus['statusId']) { STATUS_DUMMY.splice(statusIndex, 1); } +// 특정 프로젝트의 모든 상태 삭제 +export function deleteAllProjectStatus(projectId: Project['projectId']) { + const filteredStatuses = STATUS_DUMMY.filter((status) => status.projectId !== projectId); + if (filteredStatuses.length !== STATUS_DUMMY.length) { + STATUS_DUMMY.length = 0; + STATUS_DUMMY.push(...filteredStatuses); + } +} + // 프로젝트 상태 순서 재정렬 export function reorderStatusByProject(projectId: Project['projectId']) { STATUS_DUMMY.filter((status) => status.projectId === projectId) @@ -89,6 +133,17 @@ export function reorderStatusByProject(projectId: Project['projectId']) { }); } +/* ============ 일정에 연결된 유저(Task User) 관련 처리 ============ */ + +// 일정과 연결된 모든 유저 삭제 +export function deleteAllTaskUser(taskId: Task['taskId']) { + const filteredTaskUsers = TASK_USER_DUMMY.filter((taskUser) => taskUser.taskId === taskId); + if (filteredTaskUsers.length !== TASK_USER_DUMMY.length) { + TASK_USER_DUMMY.length = 0; + TASK_USER_DUMMY.push(...filteredTaskUsers); + } +} + /* ===================== 일정(Task) 관련 처리 ===================== */ // 일정 추가 @@ -126,6 +181,16 @@ export function deleteTask(taskId: Task['taskId']) { TASK_DUMMY.splice(taskIndex, 1); } +// 특정 프로젝트의 모든 일정 삭제 +export function deleteAllTask(projectId: Project['projectId']) { + const statusIds = findAllProjectStatus(projectId).map((status) => status.statusId); + const filteredTasks = TASK_DUMMY.filter((task) => !statusIds.includes(task.statusId)); + if (TASK_DUMMY.length !== filteredTasks.length) { + TASK_DUMMY.length = 0; + TASK_DUMMY.push(...filteredTasks); + } +} + // 일정 수행자 추가 export function createAssignee(assignee: TaskUser) { TASK_USER_DUMMY.push(assignee); diff --git a/src/mocks/services/projectServiceHandler.ts b/src/mocks/services/projectServiceHandler.ts index d0aa68b9..d262cfb7 100644 --- a/src/mocks/services/projectServiceHandler.ts +++ b/src/mocks/services/projectServiceHandler.ts @@ -1,13 +1,24 @@ import { http, HttpResponse } from 'msw'; import { - PROJECT_DUMMY, - PROJECT_USER_DUMMY, - STATUS_DUMMY, - TASK_DUMMY, - TASK_FILE_DUMMY, - TASK_USER_DUMMY, -} from '@mocks/mockData'; -import { getRoleHash, getUserHash } from '@mocks/mockHash'; + deleteAllProjectStatus, + deleteAllProjectUser, + deleteAllTask, + deleteAllTaskFile, + deleteAllTaskFileInMemory, + deleteAllTaskUser, + deleteProject, + findAllProject, + findAllProjectStatus, + findAllProjectUser, + findAllTask, + findProject, + findProjectUser, + findRole, + findTeamUser, + findUser, +} from '@mocks/mockAPI'; +import { convertTokenToUserId } from '@utils/converter'; +import type { SearchUser, UserWithRole } from '@/types/UserType'; const BASE_URL = import.meta.env.VITE_BASE_URL; @@ -17,113 +28,136 @@ const projectServiceHandler = [ const url = new URL(request.url); const nickname = url.searchParams.get('nickname'); const accessToken = request.headers.get('Authorization'); - const { projectId } = params; + const projectId = Number(params.projectId); + // 유저 인증 확인 if (!accessToken) return new HttpResponse(null, { status: 401 }); - // 프로젝트에 속하는 모든 유저 검색 - const projectUserList = PROJECT_USER_DUMMY.filter((row) => row.projectId === Number(projectId)); - const userHash = getUserHash(); + // 유저 ID 정보 취득 + const userId = convertTokenToUserId(accessToken); + if (!userId) return new HttpResponse(null, { status: 401 }); - const userList = projectUserList.map((relation) => { - const { userId, nickname } = userHash[relation.userId]; - return { userId, nickname }; - }); + // 유저의 프로젝트 접근 권한 확인 + const projectUser = findProjectUser(projectId, userId); + if (!projectUser) return new HttpResponse(null, { status: 403 }); + + // 프로젝트에 속하는 모든 유저 검색 + const projectUsers = findAllProjectUser(projectId); + + // 프로젝트 유저 정보 취득 + const searchUsers: SearchUser[] = []; + for (let i = 0; i < projectUsers.length; i++) { + const user = findUser(projectUsers[i].userId); + if (!user) return new HttpResponse(null, { status: 500 }); + searchUsers.push({ userId: user.userId, nickname: user.nickname }); + } // 접두사(nickname)과 일치하는 유저 정보 최대 5명 추출 const prefixRegex = new RegExp(`^${nickname}`); - const matchedUserList = userList.filter((user) => prefixRegex.test(user.nickname)).slice(0, 5); + const matchedSearchUsers = searchUsers.filter((user) => prefixRegex.test(user.nickname)).slice(0, 5); - return HttpResponse.json(matchedUserList); + return HttpResponse.json(matchedSearchUsers); }), // 프로젝트 목록 조회 API http.get(`${BASE_URL}/team/:teamId/project`, ({ request, params }) => { const accessToken = request.headers.get('Authorization'); - const { teamId } = params; + const teamId = Number(params.teamId); + // 유저 인증 확인 if (!accessToken) return new HttpResponse(null, { status: 401 }); - const projectList = PROJECT_DUMMY.filter((project) => project.teamId === Number(teamId)); + // 유저 ID 정보 취득 + const userId = convertTokenToUserId(accessToken); + if (!userId) return new HttpResponse(null, { status: 401 }); + + // 유저의 팀 접근 권한 확인 + const teamUser = findTeamUser(teamId, userId); + if (!teamUser) return new HttpResponse(null, { status: 403 }); - return HttpResponse.json(projectList); + // 특정 팀의 모든 프로젝트 조회 + const projects = findAllProject(teamId); + return HttpResponse.json(projects); }), // 프로젝트 팀원 목록 조회 API http.get(`${BASE_URL}/project/:projectId/user`, ({ request, params }) => { const accessToken = request.headers.get('Authorization'); - const { projectId } = params; + const projectId = Number(params.projectId); + // 유저 인증 확인 if (!accessToken) return new HttpResponse(null, { status: 401 }); - const projectUserList = PROJECT_USER_DUMMY.filter((projectUser) => projectUser.projectId === Number(projectId)); + // 유저 ID 정보 취득 + const userId = convertTokenToUserId(accessToken); + if (!userId) return new HttpResponse(null, { status: 401 }); - const userHash = getUserHash(); - const roleHash = getRoleHash(); - const userRoleList = projectUserList.map((projectUser) => { - const { userId, nickname } = userHash[projectUser.userId]; - const { roleName } = roleHash[projectUser.roleId]; - return { userId, nickname, roleName }; - }); + // 프로젝트 정보 취득 + const project = findProject(projectId); + if (!project) return new HttpResponse(null, { status: 404 }); + + // 유저의 팀 접근 권한 확인 + const teamUser = findTeamUser(projectId, userId); + if (!teamUser) return new HttpResponse(null, { status: 403 }); + + // 프로젝트의 모든 유저 조회 + const projectUsers = findAllProjectUser(projectId); + + // 프로젝트에 포함된 유저 정보 취득 + const userRoles: UserWithRole[] = []; + for (let i = 0; i < projectUsers.length; i++) { + const { userId, roleId } = projectUsers[i]; + + const user = findUser(userId); + const role = findRole(roleId); + if (!user || !role) return new HttpResponse(null, { status: 500 }); - return HttpResponse.json(userRoleList); + userRoles.push({ userId, nickname: user.nickname, roleName: role.roleName }); + } + + return HttpResponse.json(userRoles); }), // 프로젝트 삭제 API http.delete(`${BASE_URL}/project/:projectId`, ({ request, params }) => { const accessToken = request.headers.get('Authorization'); - const { projectId } = params; + const projectId = Number(params.projectId); + // 유저 인증 확인 if (!accessToken) return new HttpResponse(null, { status: 401 }); - const projectIdToDelete = Number(projectId); - - const statusIdsToDelete = STATUS_DUMMY.filter((status) => status.projectId === projectIdToDelete).map( - (status) => status.statusId, - ); + // 유저 ID 정보 취득 + const userId = convertTokenToUserId(accessToken); + if (!userId) return new HttpResponse(null, { status: 401 }); - const filteredProjects = PROJECT_DUMMY.filter((project) => project.projectId !== projectIdToDelete); - if (PROJECT_DUMMY.length !== filteredProjects.length) { - PROJECT_DUMMY.length = 0; - PROJECT_DUMMY.push(...filteredProjects); - } + // 유저의 프로젝트 접근 권한 확인 + const projectUser = findProjectUser(projectId, userId); + if (!projectUser) return new HttpResponse(null, { status: 403 }); - const filteredProjectUsers = PROJECT_USER_DUMMY.filter( - (projectUser) => projectUser.projectId !== projectIdToDelete, - ); - if (PROJECT_USER_DUMMY.length !== filteredProjectUsers.length) { - PROJECT_USER_DUMMY.length = 0; - PROJECT_USER_DUMMY.push(...filteredProjectUsers); - } + // 프로젝트의 모든 프로젝트 상태 ID 정보 취득 + const statusIds = findAllProjectStatus(projectId).map((status) => status.statusId); - const filteredStatuses = STATUS_DUMMY.filter((status) => status.projectId !== projectIdToDelete); - if (STATUS_DUMMY.length !== filteredStatuses.length) { - STATUS_DUMMY.length = 0; - STATUS_DUMMY.push(...filteredStatuses); - } - - const filteredTasks = TASK_DUMMY.filter((task) => !statusIdsToDelete.includes(task.statusId)); - if (TASK_DUMMY.length !== filteredTasks.length) { - TASK_DUMMY.length = 0; - TASK_DUMMY.push(...filteredTasks); - } - - const filteredTaskUsers = TASK_USER_DUMMY.filter((taskUser) => { - const taskExists = TASK_DUMMY.some((task) => task.taskId === taskUser.taskId); - return taskExists; + // 프로젝트의 모든 일정 ID 정보 취득 + const taskIds: number[] = []; + statusIds.forEach((statusId) => { + const tasks = findAllTask(statusId); + tasks.forEach((task) => taskIds.push(task.taskId)); }); - if (TASK_USER_DUMMY.length !== filteredTaskUsers.length) { - TASK_USER_DUMMY.length = 0; - TASK_USER_DUMMY.push(...filteredTaskUsers); - } - const filteredTaskFiles = TASK_FILE_DUMMY.filter((taskFile) => { - const taskExists = TASK_DUMMY.some((task) => task.taskId === taskFile.taskId); - return taskExists; - }); - if (TASK_FILE_DUMMY.length !== filteredTaskFiles.length) { - TASK_FILE_DUMMY.length = 0; - TASK_FILE_DUMMY.push(...filteredTaskFiles); + // 프로젝트 삭제(순서 중요) + try { + taskIds.forEach((taskId) => { + deleteAllTaskFileInMemory(taskId); + deleteAllTaskFile(taskId); + deleteAllTaskUser(taskId); + }); + deleteAllTask(projectId); + deleteAllProjectStatus(projectId); + deleteProject(projectId); + deleteAllProjectUser(projectId); + } catch (error) { + console.error((error as Error).message); + return new HttpResponse(null, { status: 500 }); } return new HttpResponse(null, { status: 204 });