diff --git a/backend/src/project/dto/setting-page/InitSettingResponse.dto.ts b/backend/src/project/dto/setting-page/InitSettingResponse.dto.ts index 9019099e..fdc48ce9 100644 --- a/backend/src/project/dto/setting-page/InitSettingResponse.dto.ts +++ b/backend/src/project/dto/setting-page/InitSettingResponse.dto.ts @@ -1,4 +1,5 @@ import { Member } from 'src/member/entity/member.entity'; +import { ProjectJoinRequest } from 'src/project/entity/project-join-request.entity'; import { Project } from 'src/project/entity/project.entity'; import { MemberRole } from 'src/project/enum/MemberRole.enum'; @@ -30,14 +31,38 @@ class ProjectDto { } } +class JoinRequestListDto { + id: number; + memberId: number; + username: string; + imageUrl: string; + + static of(projectJoinRequest: ProjectJoinRequest): JoinRequestListDto { + const dto = new JoinRequestListDto(); + dto.id = projectJoinRequest.id; + dto.memberId = projectJoinRequest.memberId; + dto.username = projectJoinRequest.member.username; + dto.imageUrl = projectJoinRequest.member.github_image_url; + return dto; + } +} + class ProjectInfoDto { project: ProjectDto; member: ProjectMemberDto[]; + joinRequestList: JoinRequestListDto[]; - static of(project: Project, memberList: Member[]): ProjectInfoDto { + static of( + project: Project, + memberList: Member[], + joinRequestList: ProjectJoinRequest[], + ): ProjectInfoDto { const dto = new ProjectInfoDto(); dto.project = ProjectDto.of(project); dto.member = memberList.map((member) => ProjectMemberDto.of(member)); + dto.joinRequestList = joinRequestList.map((joinRequest) => + JoinRequestListDto.of(joinRequest), + ); return dto; } } @@ -47,11 +72,15 @@ export class InitSettingResponseDto { action: string; content: ProjectInfoDto; - static of(project: Project, memberList: Member[]): InitSettingResponseDto { + static of( + project: Project, + memberList: Member[], + joinRequestList: ProjectJoinRequest[], + ): InitSettingResponseDto { const dto = new InitSettingResponseDto(); dto.domain = 'setting'; dto.action = 'init'; - dto.content = ProjectInfoDto.of(project, memberList); + dto.content = ProjectInfoDto.of(project, memberList, joinRequestList); return dto; } } diff --git a/backend/src/project/project.repository.ts b/backend/src/project/project.repository.ts index 303a8d9b..a13309ac 100644 --- a/backend/src/project/project.repository.ts +++ b/backend/src/project/project.repository.ts @@ -123,6 +123,15 @@ export class ProjectRepository { } } + async getProjectJoinRequestListWithMember( + projectId: number, + ): Promise { + return this.projectJoinRequestRepository.find({ + where: { projectId }, + relations: { member: true }, + }); + } + getProject(projectId: number): Promise { return this.projectRepository.findOne({ where: { id: projectId } }); } diff --git a/backend/src/project/service/project.service.ts b/backend/src/project/service/project.service.ts index 27d9d64c..9596427e 100644 --- a/backend/src/project/service/project.service.ts +++ b/backend/src/project/service/project.service.ts @@ -174,6 +174,21 @@ export class ProjectService { } } + async getProjectJoinRequestList( + projectId: number, + member: Member, + ): Promise { + if (!(await this.isExistProject(projectId))) { + throw new Error('Project not found'); + } + if (!(await this.isProjectLeader(projectId, member))) { + throw new Error('Member is not the project leader'); + } + return this.projectRepository.getProjectJoinRequestListWithMember( + projectId, + ); + } + async createMemo(project: Project, member: Member, color: memoColor) { const newMemo = Memo.of(project, member, '', '', color); return this.projectRepository.createMemo(newMemo); diff --git a/backend/src/project/ws-controller/ws-project.controller.ts b/backend/src/project/ws-controller/ws-project.controller.ts index fa07776b..96fd3999 100644 --- a/backend/src/project/ws-controller/ws-project.controller.ts +++ b/backend/src/project/ws-controller/ws-project.controller.ts @@ -79,14 +79,18 @@ export class WsProjectController { client.leave('backlog'); client.join('setting'); - const [project, projectMemberList] = await Promise.all([ + const [project, projectMemberList, joinRequestList] = await Promise.all([ this.projectService.getProject(client.projectId, client.member), this.projectService.getProjectMemberList(client.project), + this.projectService.getProjectJoinRequestList( + client.projectId, + client.member, + ), ]); client.emit( 'setting', - InitSettingResponseDto.of(project, projectMemberList), + InitSettingResponseDto.of(project, projectMemberList, joinRequestList), ); } } diff --git a/backend/test/project/ws-setting-page/ws-join-request-setting-page.e2e-spec.ts b/backend/test/project/ws-setting-page/ws-join-request-setting-page.e2e-spec.ts new file mode 100644 index 00000000..3f4a2af9 --- /dev/null +++ b/backend/test/project/ws-setting-page/ws-join-request-setting-page.e2e-spec.ts @@ -0,0 +1,101 @@ +import { Socket } from 'socket.io-client'; +import * as request from 'supertest'; +import { + app, + appInit, + connectServer, + createMember, + createProject, + getMemberByAccessToken, + getProjectLinkId, + listenAppAndSetPortEnv, + memberFixture, + memberFixture2, + projectPayload, +} from 'test/setup'; +import { Member } from 'src/member/entity/member.entity'; + +describe('WS Setting', () => { + beforeEach(async () => { + await app.close(); + await appInit(); + await listenAppAndSetPortEnv(app); + }); + + it('should return join request data when leader enters setting page', async () => { + const { accessToken: leaderAccessToken } = await createMember( + memberFixture, + app, + ); + + const { id: projectId } = await createProject( + leaderAccessToken, + projectPayload, + app, + ); + + const { accessToken: requestingAccessToken } = await createMember( + memberFixture2, + app, + ); + + await submitJoinRequest( + leaderAccessToken, + projectId, + requestingAccessToken, + ); + const requestingMember = await getMemberByAccessToken( + requestingAccessToken, + ); + + const leaderSocket = await enterSettingPage(projectId, leaderAccessToken); + await expectJoinRequestList(leaderSocket, requestingMember); + closePage(leaderSocket); + }); + + async function enterSettingPage(projectId: number, accessToken: string) { + const socket = connectServer(projectId, accessToken); + socket.emit('joinSetting'); + return socket; + } + + async function expectJoinRequestList( + socket: Socket, + requestingMember: Member, + ) { + return new Promise((resolve) => { + socket.on('setting', (data) => { + const { action, domain, content } = data; + expect(domain).toBe('setting'); + expect(action).toBe('init'); + expect(content.joinRequestList).toBeDefined(); + expect(content.joinRequestList.length).toBe(1); + expect(content.joinRequestList[0].id).toBeDefined(); + expect(content.joinRequestList[0].memberId).toBe(requestingMember.id); + expect(content.joinRequestList[0].username).toBe( + requestingMember.username, + ); + expect(content.joinRequestList[0].imageUrl).toBe( + requestingMember.github_image_url, + ); + resolve(); + }); + }); + } + + function closePage(socket: Socket) { + socket.close(); + } +}); + +async function submitJoinRequest( + leaderAccessToken: string, + projectId: number, + requestingAccessToken: string, +): Promise { + const inviteLinkId = await getProjectLinkId(leaderAccessToken, projectId); + return request(app.getHttpServer()) + .post('/api/project/join-request') + .set('Authorization', `Bearer ${requestingAccessToken}`) + .send({ inviteLinkId }); +}