feat: add unlimited workspace support (#5523)

fix AFF-505
This commit is contained in:
DarkSky 2024-01-10 07:28:52 +00:00
parent a59fe1b49e
commit 0d7ffb0511
No known key found for this signature in database
GPG Key ID: 97B7D036B1566E9D
7 changed files with 64 additions and 16 deletions

View File

@ -42,9 +42,22 @@ export class EarlyAccessFeatureConfig extends FeatureConfig {
}
}
export class UnlimitedWorkspaceFeatureConfig extends FeatureConfig {
override config!: Feature & { feature: FeatureType.UnlimitedWorkspace };
constructor(data: any) {
super(data);
if (this.config.feature !== FeatureType.UnlimitedWorkspace) {
throw new Error('Invalid feature config: type is not EarlyAccess');
}
}
}
const FeatureConfigMap = {
[FeatureType.Copilot]: CopilotFeatureConfig,
[FeatureType.EarlyAccess]: EarlyAccessFeatureConfig,
[FeatureType.UnlimitedWorkspace]: UnlimitedWorkspaceFeatureConfig,
};
export type FeatureConfigType<F extends FeatureType> = InstanceType<

View File

@ -3,6 +3,7 @@ import { registerEnumType } from '@nestjs/graphql';
export enum FeatureType {
Copilot = 'copilot',
EarlyAccess = 'early_access',
UnlimitedWorkspace = 'unlimited_workspace',
}
registerEnumType(FeatureType, {

View File

@ -3,6 +3,7 @@ import { z } from 'zod';
import { FeatureType } from './common';
import { featureCopilot } from './copilot';
import { featureEarlyAccess } from './early-access';
import { featureUnlimitedWorkspace } from './unlimited-workspace';
/// ======== common schema ========
@ -43,6 +44,12 @@ export const Features: Feature[] = [
version: 2,
configs: {},
},
{
feature: FeatureType.UnlimitedWorkspace,
type: FeatureKind.Feature,
version: 1,
configs: {},
},
];
/// ======== schema infer ========
@ -51,7 +58,13 @@ export const FeatureSchema = commonFeatureSchema
.extend({
type: z.literal(FeatureKind.Feature),
})
.and(z.discriminatedUnion('feature', [featureCopilot, featureEarlyAccess]));
.and(
z.discriminatedUnion('feature', [
featureCopilot,
featureEarlyAccess,
featureUnlimitedWorkspace,
])
);
export type Feature = z.infer<typeof FeatureSchema>;

View File

@ -0,0 +1,8 @@
import { z } from 'zod';
import { FeatureType } from './common';
export const featureUnlimitedWorkspace = z.object({
feature: z.literal(FeatureType.UnlimitedWorkspace),
configs: z.object({}),
});

View File

@ -28,6 +28,7 @@ import type { FileUpload } from '../../../types';
import { Auth, CurrentUser, Public } from '../../auth';
import { MailService } from '../../auth/mailer';
import { AuthService } from '../../auth/service';
import { FeatureManagementService, FeatureType } from '../../features';
import { QuotaManagementService } from '../../quota';
import { WorkspaceBlobStorage } from '../../storage';
import { UsersService, UserType } from '../../users';
@ -57,6 +58,7 @@ export class WorkspaceResolver {
private readonly mailer: MailService,
private readonly prisma: PrismaService,
private readonly permissions: PermissionService,
private readonly feature: FeatureManagementService,
private readonly quota: QuotaManagementService,
private readonly users: UsersService,
private readonly event: EventEmitter,
@ -325,20 +327,26 @@ export class WorkspaceResolver {
throw new ForbiddenException('Cannot change owner');
}
// member limit check
const [memberCount, quota] = await Promise.all([
this.prisma.workspaceUserPermission.count({
where: { workspaceId },
}),
this.quota.getUserQuota(user.id),
]);
if (memberCount >= quota.memberLimit) {
throw new GraphQLError('Workspace member limit reached', {
extensions: {
status: HttpStatus[HttpStatus.PAYLOAD_TOO_LARGE],
code: HttpStatus.PAYLOAD_TOO_LARGE,
},
});
const unlimited = await this.feature.hasWorkspaceFeature(
workspaceId,
FeatureType.UnlimitedWorkspace
);
if (!unlimited) {
// member limit check
const [memberCount, quota] = await Promise.all([
this.prisma.workspaceUserPermission.count({
where: { workspaceId },
}),
this.quota.getUserQuota(user.id),
]);
if (memberCount >= quota.memberLimit) {
throw new GraphQLError('Workspace member limit reached', {
extensions: {
status: HttpStatus[HttpStatus.PAYLOAD_TOO_LARGE],
code: HttpStatus.PAYLOAD_TOO_LARGE,
},
});
}
}
let target = await this.users.findUserByEmail(email);

View File

@ -152,6 +152,7 @@ type WorkspaceType {
enum FeatureType {
Copilot
EarlyAccess
UnlimitedWorkspace
}
type InvitationWorkspaceType {

View File

@ -129,7 +129,11 @@ test.beforeEach(async t => {
.overrideProvider(PrismaService)
.useValue(FakePrisma)
.overrideProvider(FeatureManagementService)
.useValue({})
.useValue({
hasWorkspaceFeature() {
return false;
},
})
.compile();
const app = module.createNestApplication();
app.use(