Skip to content

Commit

Permalink
Refactor: #224 MSW ProjectSevice 기능 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
Seok93 committed Oct 17, 2024
1 parent cfdaf5b commit 161fd91
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 78 deletions.
71 changes: 68 additions & 3 deletions src/mocks/mockAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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) 관련 처리 ================ */

// 프로젝트 상태 정보 생성
Expand Down Expand Up @@ -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)
Expand All @@ -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) 관련 처리 ===================== */

// 일정 추가
Expand Down Expand Up @@ -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);
Expand Down
184 changes: 109 additions & 75 deletions src/mocks/services/projectServiceHandler.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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 });
Expand Down

0 comments on commit 161fd91

Please sign in to comment.