mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
feat(server): send review request when team member count over quota limit (#11126)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import {
|
||||
acceptInviteByInviteIdMutation,
|
||||
createInviteLinkMutation,
|
||||
WorkspaceInviteLinkExpireTime,
|
||||
} from '@affine/graphql';
|
||||
|
||||
import { Mockers } from '../../mocks';
|
||||
import { app, e2e } from '../test';
|
||||
|
||||
async function createTeamWorkspace(quantity = 10) {
|
||||
const owner = await app.create(Mockers.User);
|
||||
|
||||
const workspace = await app.create(Mockers.Workspace, {
|
||||
owner: { id: owner.id },
|
||||
});
|
||||
await app.create(Mockers.TeamWorkspace, {
|
||||
id: workspace.id,
|
||||
quantity,
|
||||
});
|
||||
|
||||
return {
|
||||
owner,
|
||||
workspace,
|
||||
};
|
||||
}
|
||||
|
||||
e2e(
|
||||
'should invite by link and send review request notification below quota limit',
|
||||
async t => {
|
||||
const { owner, workspace } = await createTeamWorkspace();
|
||||
|
||||
await app.login(owner);
|
||||
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 inviteId = link.split('/').pop()!;
|
||||
|
||||
// accept invite by link
|
||||
await app.signup();
|
||||
const result = await app.gql({
|
||||
query: acceptInviteByInviteIdMutation,
|
||||
variables: {
|
||||
workspaceId: workspace.id,
|
||||
inviteId,
|
||||
},
|
||||
});
|
||||
t.truthy(result, 'failed to accept invite');
|
||||
const notification = app.queue.last(
|
||||
'notification.sendInvitationReviewRequest'
|
||||
);
|
||||
t.is(notification.payload.reviewerId, owner.id);
|
||||
t.truthy(notification.payload.inviteId);
|
||||
}
|
||||
);
|
||||
|
||||
e2e(
|
||||
'should invite by link and send review request notification over quota limit',
|
||||
async t => {
|
||||
const { owner, workspace } = await createTeamWorkspace(1);
|
||||
|
||||
await app.login(owner);
|
||||
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 inviteId = link.split('/').pop()!;
|
||||
|
||||
// accept invite by link
|
||||
await app.signup();
|
||||
const result = await app.gql({
|
||||
query: acceptInviteByInviteIdMutation,
|
||||
variables: {
|
||||
workspaceId: workspace.id,
|
||||
inviteId,
|
||||
},
|
||||
});
|
||||
t.truthy(result, 'failed to accept invite');
|
||||
const notification = app.queue.last(
|
||||
'notification.sendInvitationReviewRequest'
|
||||
);
|
||||
t.is(notification.payload.reviewerId, owner.id);
|
||||
t.truthy(notification.payload.inviteId);
|
||||
}
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { Feature } from '../../models';
|
||||
import { Feature, FeatureType } from '../../models';
|
||||
import { Mocker } from './factory';
|
||||
|
||||
interface MockTeamWorkspaceInput {
|
||||
@@ -46,6 +46,8 @@ export class MockTeamWorkspace extends Mocker<
|
||||
featureId: feature.id,
|
||||
reason: 'test',
|
||||
activated: true,
|
||||
name: Feature.TeamPlan,
|
||||
type: FeatureType.Quota,
|
||||
configs: {
|
||||
memberLimit: quantity,
|
||||
},
|
||||
|
||||
@@ -610,38 +610,8 @@ export class WorkspaceResolver {
|
||||
`workspace:inviteLink:${workspaceId}`
|
||||
);
|
||||
if (invite?.inviteId === inviteId) {
|
||||
const seatAvailable = await this.quota.tryCheckSeat(workspaceId);
|
||||
if (seatAvailable) {
|
||||
const invite = await this.models.workspaceUser.set(
|
||||
workspaceId,
|
||||
user.id,
|
||||
WorkspaceRole.Collaborator,
|
||||
WorkspaceMemberStatus.UnderReview
|
||||
);
|
||||
await this.workspaceService.sendReviewRequestNotification(invite.id);
|
||||
return true;
|
||||
} else {
|
||||
const isTeam =
|
||||
await this.workspaceService.isTeamWorkspace(workspaceId);
|
||||
// only team workspace allow over limit
|
||||
if (isTeam) {
|
||||
await this.models.workspaceUser.set(
|
||||
workspaceId,
|
||||
user.id,
|
||||
WorkspaceRole.Collaborator,
|
||||
WorkspaceMemberStatus.NeedMoreSeatAndReview
|
||||
);
|
||||
const memberCount =
|
||||
await this.models.workspaceUser.count(workspaceId);
|
||||
this.event.emit('workspace.members.updated', {
|
||||
workspaceId,
|
||||
count: memberCount,
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
throw new MemberQuotaExceeded();
|
||||
}
|
||||
}
|
||||
await this.acceptInviteByLink(user, workspaceId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,6 +620,40 @@ export class WorkspaceResolver {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async acceptInviteByLink(user: CurrentUser, workspaceId: string) {
|
||||
const seatAvailable = await this.quota.tryCheckSeat(workspaceId);
|
||||
if (seatAvailable) {
|
||||
const role = await this.models.workspaceUser.set(
|
||||
workspaceId,
|
||||
user.id,
|
||||
WorkspaceRole.Collaborator,
|
||||
WorkspaceMemberStatus.UnderReview
|
||||
);
|
||||
await this.workspaceService.sendReviewRequestNotification(role.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const isTeam = await this.workspaceService.isTeamWorkspace(workspaceId);
|
||||
// only team workspace allow over limit
|
||||
if (isTeam) {
|
||||
const role = await this.models.workspaceUser.set(
|
||||
workspaceId,
|
||||
user.id,
|
||||
WorkspaceRole.Collaborator,
|
||||
WorkspaceMemberStatus.NeedMoreSeatAndReview
|
||||
);
|
||||
await this.workspaceService.sendReviewRequestNotification(role.id);
|
||||
const memberCount = await this.models.workspaceUser.count(workspaceId);
|
||||
this.event.emit('workspace.members.updated', {
|
||||
workspaceId,
|
||||
count: memberCount,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
throw new MemberQuotaExceeded();
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async leaveWorkspace(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
|
||||
Reference in New Issue
Block a user