diff --git a/frontend/src/component/admin/instance-privacy/InstancePrivacy.tsx b/frontend/src/component/admin/instance-privacy/InstancePrivacy.tsx index e2c08f240d..cc78850d0d 100644 --- a/frontend/src/component/admin/instance-privacy/InstancePrivacy.tsx +++ b/frontend/src/component/admin/instance-privacy/InstancePrivacy.tsx @@ -44,6 +44,8 @@ const featureCollectionDetails = { Roles: 'The number of custom roles defined in your instance', 'Custom Root Roles': 'The number of custom root roles defined in your instance', + 'Custom Root Roles In Use': + 'The number of custom root roles that are in use by entities (users, groups, service accounts)', Environments: 'The number of environments in your instance', Segments: 'The number of segments defined in your instance', Strategies: 'The number of strategies defined in your instance', diff --git a/src/lib/db/role-store.ts b/src/lib/db/role-store.ts index 104d72329d..f5386b4e68 100644 --- a/src/lib/db/role-store.ts +++ b/src/lib/db/role-store.ts @@ -64,6 +64,19 @@ export default class RoleStore implements IRoleStore { .then((res) => Number(res[0].count)); } + async filteredCountInUse(filter: Partial): Promise { + return this.db + .from(T.ROLES) + .countDistinct('roles.id') + .leftJoin('role_user as ru', 'roles.id', 'ru.role_id') + .leftJoin('groups as g', 'roles.id', 'g.root_role_id') + .where(filter) + .andWhere((qb) => + qb.whereNotNull('ru.role_id').orWhereNotNull('g.root_role_id'), + ) + .then((res) => Number(res[0].count)); + } + async create(role: ICustomRoleInsert): Promise { const row = await this.db(T.ROLES) .insert({ diff --git a/src/lib/metrics.ts b/src/lib/metrics.ts index 6249e6a848..ee8ee790e9 100644 --- a/src/lib/metrics.ts +++ b/src/lib/metrics.ts @@ -109,6 +109,11 @@ export default class MetricsMonitor { help: 'Number of custom root roles', }); + const customRootRolesInUseTotal = new client.Gauge({ + name: 'custom_root_roles_in_use_total', + help: 'Number of custom root roles in use', + }); + const segmentsTotal = new client.Gauge({ name: 'segments_total', help: 'Number of segments', @@ -177,6 +182,9 @@ export default class MetricsMonitor { customRootRolesTotal.reset(); customRootRolesTotal.set(stats.customRootRoles); + customRootRolesInUseTotal.reset(); + customRootRolesInUseTotal.set(stats.customRootRolesInUse); + segmentsTotal.reset(); segmentsTotal.set(stats.segments); diff --git a/src/lib/routes/admin-api/instance-admin.ts b/src/lib/routes/admin-api/instance-admin.ts index 32942069bf..94c17bb9a8 100644 --- a/src/lib/routes/admin-api/instance-admin.ts +++ b/src/lib/routes/admin-api/instance-admin.ts @@ -99,7 +99,8 @@ class InstanceAdminController extends Controller { instanceId: 'ed3861ae-78f9-4e8c-8e57-b57efc15f82b', projects: 1, roles: 5, - customRootRoles: 1, + customRootRoles: 2, + customRootRolesInUse: 1, segments: 2, strategies: 8, sum: 'some-sha256-hash', diff --git a/src/lib/services/instance-stats-service.ts b/src/lib/services/instance-stats-service.ts index 70cbe20b19..9116dbacc1 100644 --- a/src/lib/services/instance-stats-service.ts +++ b/src/lib/services/instance-stats-service.ts @@ -33,6 +33,7 @@ export interface InstanceStats { contextFields: number; roles: number; customRootRoles: number; + customRootRolesInUse: number; featureExports: number; featureImports: number; groups: number; @@ -180,6 +181,7 @@ export class InstanceStatsService { groups, roles, customRootRoles, + customRootRolesInUse, environments, segments, strategies, @@ -196,6 +198,7 @@ export class InstanceStatsService { this.groupStore.count(), this.roleStore.count(), this.roleStore.filteredCount({ type: CUSTOM_ROOT_ROLE_TYPE }), + this.roleStore.filteredCountInUse({ type: CUSTOM_ROOT_ROLE_TYPE }), this.environmentStore.count(), this.segmentStore.count(), this.strategyStore.count(), @@ -217,6 +220,7 @@ export class InstanceStatsService { contextFields, roles, customRootRoles, + customRootRolesInUse, groups, environments, segments, diff --git a/src/lib/services/version-service.ts b/src/lib/services/version-service.ts index 84759dd8b7..3b9f6782c6 100644 --- a/src/lib/services/version-service.ts +++ b/src/lib/services/version-service.ts @@ -229,6 +229,7 @@ export default class VersionService { groups, roles, customRootRoles, + customRootRolesInUse, environments, segments, strategies, @@ -248,6 +249,7 @@ export default class VersionService { this.roleStore.filteredCount({ type: CUSTOM_ROOT_ROLE_TYPE, }), + this.roleStore.filteredCountInUse({ type: CUSTOM_ROOT_ROLE_TYPE }), this.environmentStore.count(), this.segmentStore.count(), this.strategyStore.count(), @@ -269,6 +271,7 @@ export default class VersionService { groups, roles, customRootRoles, + customRootRolesInUse, environments, segments, strategies, diff --git a/src/lib/types/stores/role-store.ts b/src/lib/types/stores/role-store.ts index 1bc367aa6c..27ca9b7f6f 100644 --- a/src/lib/types/stores/role-store.ts +++ b/src/lib/types/stores/role-store.ts @@ -31,4 +31,5 @@ export interface IRoleStore extends Store { nameInUse(name: string, existingId?: number): Promise; count(): Promise; filteredCount(filter: Partial): Promise; + filteredCountInUse(filter: Partial): Promise; } diff --git a/src/test/fixtures/fake-role-store.ts b/src/test/fixtures/fake-role-store.ts index bcf22bea67..034d4586c2 100644 --- a/src/test/fixtures/fake-role-store.ts +++ b/src/test/fixtures/fake-role-store.ts @@ -17,6 +17,10 @@ export default class FakeRoleStore implements IRoleStore { return Promise.resolve(0); } + filteredCountInUse(search: Partial): Promise { + return Promise.resolve(0); + } + roles: ICustomRole[] = []; getGroupRolesForProject(projectId: string): Promise {