From f1605e246b4ee86fb79a2c720bd100c0cd4ebc29 Mon Sep 17 00:00:00 2001 From: forehalo Date: Wed, 30 Apr 2025 09:47:50 +0000 Subject: [PATCH] feat(server): support query overcapacity member count of workspace (#12050) ## Summary by CodeRabbit - **New Features** - Workspace quota information now includes a new metric: the count of members exceeding seat capacity ("overcapacityMemberCount"), available in both numeric and human-readable formats. - The workspace quota display and related queries have been updated to show this new overcapacity member count alongside existing quota metrics. --- packages/backend/server/src/core/quota/service.ts | 7 ++++++- packages/backend/server/src/core/quota/types.ts | 6 ++++++ packages/backend/server/src/models/workspace-user.ts | 9 +++++++++ packages/backend/server/src/schema.gql | 2 ++ packages/common/graphql/src/graphql/index.ts | 3 +++ packages/common/graphql/src/graphql/workspace-quota.gql | 3 +++ packages/common/graphql/src/schema.ts | 5 +++++ 7 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/backend/server/src/core/quota/service.ts b/packages/backend/server/src/core/quota/service.ts index fa519809f..54969d448 100644 --- a/packages/backend/server/src/core/quota/service.ts +++ b/packages/backend/server/src/core/quota/service.ts @@ -142,12 +142,16 @@ export class QuotaService { const usedStorageQuota = quota.ownerQuota ? await this.getUserStorageUsage(quota.ownerQuota) : await this.getWorkspaceStorageUsage(workspaceId); - const memberCount = await this.models.workspaceUser.count(workspaceId); + const memberCount = + await this.models.workspaceUser.chargedCount(workspaceId); + const overcapacityMemberCount = + await this.models.workspaceUser.insufficientSeatMemberCount(workspaceId); return { ...quota, usedStorageQuota, memberCount, + overcapacityMemberCount, usedSize: usedStorageQuota, }; } @@ -203,6 +207,7 @@ export class QuotaService { historyPeriod: formatDate(quota.historyPeriod), memberLimit: quota.memberLimit.toString(), memberCount: quota.memberCount.toString(), + overcapacityMemberCount: quota.overcapacityMemberCount.toString(), }; } diff --git a/packages/backend/server/src/core/quota/types.ts b/packages/backend/server/src/core/quota/types.ts index fbe6f41dd..31da198ed 100644 --- a/packages/backend/server/src/core/quota/types.ts +++ b/packages/backend/server/src/core/quota/types.ts @@ -85,6 +85,9 @@ export class WorkspaceQuotaHumanReadableType { @Field() memberCount!: string; + + @Field() + overcapacityMemberCount!: string; } @ObjectType() @@ -110,6 +113,9 @@ export class WorkspaceQuotaType implements Partial { @Field() memberCount!: number; + @Field() + overcapacityMemberCount!: number; + @Field() humanReadable!: WorkspaceQuotaHumanReadableType; diff --git a/packages/backend/server/src/models/workspace-user.ts b/packages/backend/server/src/models/workspace-user.ts index 18ea90af9..d5b47225f 100644 --- a/packages/backend/server/src/models/workspace-user.ts +++ b/packages/backend/server/src/models/workspace-user.ts @@ -289,6 +289,15 @@ export class WorkspaceUserModel extends BaseModel { }); } + async insufficientSeatMemberCount(workspaceId: string) { + return this.db.workspaceUserRole.count({ + where: { + workspaceId, + status: WorkspaceMemberStatus.NeedMoreSeat, + }, + }); + } + async getUserActiveRoles( userId: string, filter: { role?: WorkspaceRole } = {} diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index e26e0c93d..9d0a3bcac 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -1746,6 +1746,7 @@ type WorkspaceQuotaHumanReadableType { memberCount: String! memberLimit: String! name: String! + overcapacityMemberCount: String! storageQuota: String! storageQuotaUsed: String! } @@ -1757,6 +1758,7 @@ type WorkspaceQuotaType { memberCount: Int! memberLimit: Int! name: String! + overcapacityMemberCount: Int! storageQuota: SafeInt! usedSize: SafeInt! @deprecated(reason: "use `usedStorageQuota` instead") usedStorageQuota: SafeInt! diff --git a/packages/common/graphql/src/graphql/index.ts b/packages/common/graphql/src/graphql/index.ts index ae8aad635..ce1f5dff0 100644 --- a/packages/common/graphql/src/graphql/index.ts +++ b/packages/common/graphql/src/graphql/index.ts @@ -1821,12 +1821,15 @@ export const workspaceQuotaQuery = { historyPeriod memberLimit memberCount + overcapacityMemberCount humanReadable { name blobLimit storageQuota historyPeriod memberLimit + memberCount + overcapacityMemberCount } } } diff --git a/packages/common/graphql/src/graphql/workspace-quota.gql b/packages/common/graphql/src/graphql/workspace-quota.gql index 7d51730c1..0f7c76aed 100644 --- a/packages/common/graphql/src/graphql/workspace-quota.gql +++ b/packages/common/graphql/src/graphql/workspace-quota.gql @@ -8,12 +8,15 @@ query workspaceQuota($id: String!) { historyPeriod memberLimit memberCount + overcapacityMemberCount humanReadable { name blobLimit storageQuota historyPeriod memberLimit + memberCount + overcapacityMemberCount } } } diff --git a/packages/common/graphql/src/schema.ts b/packages/common/graphql/src/schema.ts index 8761c1c4e..7f48a60da 100644 --- a/packages/common/graphql/src/schema.ts +++ b/packages/common/graphql/src/schema.ts @@ -2310,6 +2310,7 @@ export interface WorkspaceQuotaHumanReadableType { memberCount: Scalars['String']['output']; memberLimit: Scalars['String']['output']; name: Scalars['String']['output']; + overcapacityMemberCount: Scalars['String']['output']; storageQuota: Scalars['String']['output']; storageQuotaUsed: Scalars['String']['output']; } @@ -2322,6 +2323,7 @@ export interface WorkspaceQuotaType { memberCount: Scalars['Int']['output']; memberLimit: Scalars['Int']['output']; name: Scalars['String']['output']; + overcapacityMemberCount: Scalars['Int']['output']; storageQuota: Scalars['SafeInt']['output']; /** @deprecated use `usedStorageQuota` instead */ usedSize: Scalars['SafeInt']['output']; @@ -4498,6 +4500,7 @@ export type WorkspaceQuotaQuery = { historyPeriod: number; memberLimit: number; memberCount: number; + overcapacityMemberCount: number; humanReadable: { __typename?: 'WorkspaceQuotaHumanReadableType'; name: string; @@ -4505,6 +4508,8 @@ export type WorkspaceQuotaQuery = { storageQuota: string; historyPeriod: string; memberLimit: string; + memberCount: string; + overcapacityMemberCount: string; }; }; };