Skip to content

Commit

Permalink
feat: 프로젝트 설정페이지 init API에 프로젝트 참여요청 정보 추가
Browse files Browse the repository at this point in the history
- 레포지토리
  - 프로젝트 참여요청 목록 조회 메서드 추가
- 서비스
  - 프로젝트 참여요청 목록 조회 메서드 추가
- 컨트롤러
  - setting page join 메서드의 반환으로 참여요청 목록 데이터 추가
- DTO
  - 설정 페이지 init DTO에 프로젝트 참여요청 정보 추가
- E2E 테스트
  - 프로젝트 참여요청 조회 E2E 테스트 추가
  • Loading branch information
choyoungwoo9 committed Oct 8, 2024
1 parent d0706f2 commit a596500
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 5 deletions.
35 changes: 32 additions & 3 deletions backend/src/project/dto/setting-page/InitSettingResponse.dto.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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;
}
}
9 changes: 9 additions & 0 deletions backend/src/project/project.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ export class ProjectRepository {
}
}

async getProjectJoinRequestListWithMember(
projectId: number,
): Promise<ProjectJoinRequest[]> {
return this.projectJoinRequestRepository.find({
where: { projectId },
relations: { member: true },
});
}

getProject(projectId: number): Promise<Project | null> {
return this.projectRepository.findOne({ where: { id: projectId } });
}
Expand Down
15 changes: 15 additions & 0 deletions backend/src/project/service/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,21 @@ export class ProjectService {
}
}

async getProjectJoinRequestList(
projectId: number,
member: Member,
): Promise<ProjectJoinRequest[]> {
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);
Expand Down
8 changes: 6 additions & 2 deletions backend/src/project/ws-controller/ws-project.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
);
}
}
Original file line number Diff line number Diff line change
@@ -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<void>((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<request.Response> {
const inviteLinkId = await getProjectLinkId(leaderAccessToken, projectId);
return request(app.getHttpServer())
.post('/api/project/join-request')
.set('Authorization', `Bearer ${requestingAccessToken}`)
.send({ inviteLinkId });
}

0 comments on commit a596500

Please sign in to comment.