Skip to content

Commit 4ff4d81

Browse files
committed
feat: 랜딩페이지 입장시 프로젝트의 리더에게만 초대링크를 제공하도록 구현
- getProject 서비스 메서드에서 리더에게만 초대링크를 제공하도록 변경 - getProject의 파라미터가 회원정보를 추가로 요구하기 영향을 받는 메서드 수정 - 클라이언트 API의 DTO인 initLandingDto에서 초대링크 정보가 있을때만 첨부하도록 변경 - 리더에게는 초대링크가 제공되고, 일반 멤버에게는 초대링크가 제공되지 않는것을 확인하는 E2E테스트 추가
1 parent 3062ed3 commit 4ff4d81

File tree

6 files changed

+79
-12
lines changed

6 files changed

+79
-12
lines changed

backend/src/project/dto/InitLandingResponse.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class ProjectLandingPageContentDto {
7979
sprint: null;
8080
memoList: MemoDto[];
8181
linkList: LinkDto[];
82-
inviteLinkId: string;
82+
inviteLinkId?: string;
8383

8484
static of(
8585
project: Project,
@@ -112,7 +112,7 @@ class ProjectLandingPageContentDto {
112112
const linkDtoList = linkList.map((link) => LinkDto.of(link));
113113
dto.linkList = linkDtoList;
114114

115-
dto.inviteLinkId = project.inviteLinkId;
115+
if (project.inviteLinkId) dto.inviteLinkId = project.inviteLinkId;
116116
return dto;
117117
}
118118
}

backend/src/project/service/project.service.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,25 @@ export class ProjectService {
4242
return await this.projectRepository.getProjectList(member);
4343
}
4444

45-
async getProject(projectId: number): Promise<Project | null> {
46-
return await this.projectRepository.getProject(projectId);
45+
private getProjectWithInviteLink(projectId: number) {
46+
return this.projectRepository.getProject(projectId);
47+
}
48+
private async getProjectWithOutInviteLink(projectId: number) {
49+
const project = await this.projectRepository.getProject(projectId);
50+
delete project.inviteLinkId;
51+
return project;
52+
}
53+
54+
async getProject(projectId: number, member: Member): Promise<Project | null> {
55+
if (!(await this.isExistProject(projectId)))
56+
throw new Error('Project not found');
57+
if (!(await this.isProjectMember(projectId, member))) {
58+
throw new Error('Not project member');
59+
}
60+
if (await this.isProjectLeader(projectId, member)) {
61+
return this.getProjectWithInviteLink(projectId);
62+
}
63+
return this.getProjectWithOutInviteLink(projectId);
4764
}
4865

4966
async addMember(project: Project, member: Member): Promise<void> {
@@ -64,6 +81,11 @@ export class ProjectService {
6481
return this.projectRepository.getProjectMemberList(project);
6582
}
6683

84+
async isExistProject(projectId: number): Promise<boolean> {
85+
const project = await this.projectRepository.getProject(projectId);
86+
return !!project;
87+
}
88+
6789
async isProjectMember(projectId: number, member: Member): Promise<boolean> {
6890
const projectToMember: ProjectToMember | null =
6991
await this.projectRepository.getProjectToMember(projectId, member);

backend/src/project/websocket.gateway.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,10 @@ export class ProjectWebsocketGateway
226226
client.projectId = parseInt(projectId[1], 10);
227227
if (isNaN(client.projectId)) throw new Error('Project is not number');
228228
}
229-
const project = await this.projectService.getProject(client.projectId);
230-
if (!project) throw new Error('Project not found');
231-
const isProjectMember = await this.projectService.isProjectMember(
232-
project.id,
229+
const project = await this.projectService.getProject(
230+
client.projectId,
233231
client.member,
234232
);
235-
if (!isProjectMember) throw new Error('Not project member');
236233
client.project = project;
237234
}
238235
}

backend/src/project/ws-controller/ws-project.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class WsProjectController {
1414
async joinLandingPage(client: ClientSocket) {
1515
const [project, projectMemberList, memoListWithMember, linkList] =
1616
await Promise.all([
17-
this.projectService.getProject(client.projectId),
17+
this.projectService.getProject(client.projectId, client.member),
1818
this.projectService.getProjectMemberList(client.project),
1919
this.projectService.getProjectMemoListWithMember(client.project.id),
2020
this.projectService.getProjectLinkList(client.project),
@@ -80,7 +80,7 @@ export class WsProjectController {
8080
client.join('setting');
8181

8282
const [project, projectMemberList] = await Promise.all([
83-
this.projectService.getProject(client.projectId),
83+
this.projectService.getProject(client.projectId, client.member),
8484
this.projectService.getProjectMemberList(client.project),
8585
]);
8686

backend/test/project/ws-landing-page/ws-project-landing-page.e2e-spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ describe('WS landing', () => {
7777
expect(content.sprint).toBeDefined();
7878
expect(content.memoList).toBeDefined();
7979
expect(content.linkList).toBeDefined();
80-
expect(content.inviteLinkId).toBeDefined();
8180

8281
resolve();
8382
});

backend/test/project/ws-landing-page/ws-role.e2e-spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,54 @@ describe('WS role', () => {
7373
});
7474
});
7575
};
76+
77+
it('should return invite link when member is project leader', async () => {
78+
let socket1;
79+
let socket2;
80+
return new Promise<void>(async (resolve, reject) => {
81+
// 회원1 회원가입 + 프로젝트 생성
82+
const accessToken = (await createMember(memberFixture, app))
83+
.accessToken;
84+
const project = await createProject(accessToken, projectPayload, app);
85+
const projectLinkId = await getProjectLinkId(accessToken, project.id);
86+
87+
// 회원2 회원가입 + 프로젝트 참여
88+
const accessToken2 = (await createMember(memberFixture2, app))
89+
.accessToken;
90+
await joinProject(accessToken2, projectLinkId);
91+
92+
socket1 = connectServer(project.id, accessToken);
93+
handleConnectErrorWithReject(socket1, reject);
94+
handleErrorWithReject(socket1, reject);
95+
await emitJoinLanding(socket1);
96+
await expectInviteLink(socket1, MemberRole.LEADER);
97+
98+
socket2 = connectServer(project.id, accessToken2);
99+
handleConnectErrorWithReject(socket2, reject);
100+
handleErrorWithReject(socket2, reject);
101+
await emitJoinLanding(socket2);
102+
await expectInviteLink(socket2, MemberRole.MEMBER);
103+
104+
resolve();
105+
}).finally(() => {
106+
socket1.close();
107+
socket2.close();
108+
});
109+
});
110+
111+
const expectInviteLink = async (socket: Socket, role: string) => {
112+
await new Promise<void>((resolve, reject) => {
113+
socket.once('landing', async (data) => {
114+
const { action, domain, content } = data;
115+
if (action === 'init' && domain === 'landing') {
116+
if (role === MemberRole.LEADER)
117+
expect(content.inviteLinkId).toBeDefined();
118+
else if (role === MemberRole.MEMBER)
119+
expect(content.inviteLinkId).toBeUndefined();
120+
resolve();
121+
} else reject();
122+
});
123+
});
124+
};
76125
});
77126
});

0 commit comments

Comments
 (0)