From 70a318f1c47a33ad44e592680fd1dadaab823dda Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Thu, 3 Apr 2025 07:19:14 +0000 Subject: [PATCH] fix(server): auto-accept pending invitations by link in team workspace (#11433) close CLOUD-192 --- .../src/__tests__/e2e/workspace/team.spec.ts | 51 +++++++++++++++++++ .../core/workspaces/resolvers/workspace.ts | 5 ++ 2 files changed, 56 insertions(+) diff --git a/packages/backend/server/src/__tests__/e2e/workspace/team.spec.ts b/packages/backend/server/src/__tests__/e2e/workspace/team.spec.ts index 4564c180b..420a9f0ee 100644 --- a/packages/backend/server/src/__tests__/e2e/workspace/team.spec.ts +++ b/packages/backend/server/src/__tests__/e2e/workspace/team.spec.ts @@ -1,7 +1,10 @@ import { acceptInviteByInviteIdMutation, createInviteLinkMutation, + getInviteInfoQuery, + inviteByEmailMutation, WorkspaceInviteLinkExpireTime, + WorkspaceMemberStatus, } from '@affine/graphql'; import { Mockers } from '../../mocks'; @@ -93,3 +96,51 @@ e2e( t.truthy(notification.payload.inviteId); } ); + +e2e( + 'should accept invitation by link directly if status is pending on team workspace', + async t => { + const { owner, workspace } = await createTeamWorkspace(2); + const member = await app.create(Mockers.User); + + await app.login(owner); + // create a pending invitation + const invite = await app.gql({ + query: inviteByEmailMutation, + variables: { + email: member.email, + workspaceId: workspace.id, + }, + }); + t.truthy(invite, 'failed to create invitation'); + + const { createInviteLink } = await app.gql({ + query: createInviteLinkMutation, + variables: { + workspaceId: workspace.id, + expireTime: WorkspaceInviteLinkExpireTime.OneDay, + }, + }); + t.truthy(createInviteLink, 'failed to create invite link'); + const link = createInviteLink.link; + const inviteLinkId = link.split('/').pop()!; + + // member accept invitation by link + await app.login(member); + await app.gql({ + query: acceptInviteByInviteIdMutation, + variables: { + inviteId: inviteLinkId, + workspaceId: workspace.id, + }, + }); + + const { getInviteInfo } = await app.gql({ + query: getInviteInfoQuery, + variables: { + inviteId: invite.invite, + }, + }); + t.is(getInviteInfo.status, WorkspaceMemberStatus.Accepted); + } +); diff --git a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts index 3cca03246..1586f86b8 100644 --- a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts +++ b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts @@ -664,6 +664,11 @@ export class WorkspaceResolver { WorkspaceRole.Collaborator, WorkspaceMemberStatus.NeedMoreSeatAndReview ); + // if status is pending, should accept the invite directly + if (role.status === WorkspaceMemberStatus.Pending) { + await this.acceptInviteByInviteId(role.id); + return; + } await this.workspaceService.sendReviewRequestNotification(role.id); const memberCount = await this.models.workspaceUser.count(workspaceId); this.event.emit('workspace.members.updated', {