diff --git a/packages/cli/src/databases/repositories/role.repository.ts b/packages/cli/src/databases/repositories/role.repository.ts index a78efadf10..8ecee595c1 100644 --- a/packages/cli/src/databases/repositories/role.repository.ts +++ b/packages/cli/src/databases/repositories/role.repository.ts @@ -12,4 +12,23 @@ export class RoleRepository extends Repository { async findRole(scope: RoleScopes, name: RoleNames) { return this.findOne({ where: { scope, name } }); } + + /** + * Counts the number of users in each role, e.g. `{ admin: 2, member: 6, owner: 1 }` + */ + async countUsersByRole() { + type Row = { role_name: string; count: number }; + + const rows: Row[] = await this.createQueryBuilder('role') + .select('role.name') + .addSelect('COUNT(user.id)', 'count') + .innerJoin('user', 'user', 'role.id = user.globalRoleId') + .groupBy('role.name') + .getRawMany(); + + return rows.reduce>((acc, item) => { + acc[item.role_name] = item.count; + return acc; + }, {}); + } } diff --git a/packages/cli/src/telemetry/index.ts b/packages/cli/src/telemetry/index.ts index 2f8d82ab11..3da3384d0b 100644 --- a/packages/cli/src/telemetry/index.ts +++ b/packages/cli/src/telemetry/index.ts @@ -11,6 +11,7 @@ import { License } from '@/License'; import { N8N_VERSION } from '@/constants'; import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { SourceControlPreferencesService } from '../environments/sourceControl/sourceControlPreferences.service.ee'; +import { RoleRepository } from '@/databases/repositories/role.repository'; type ExecutionTrackDataKey = 'manual_error' | 'manual_success' | 'prod_error' | 'prod_success'; @@ -110,6 +111,7 @@ export class Telemetry { plan_name_current: this.license.getPlanName(), quota: this.license.getTriggerLimit(), usage: await this.workflowRepository.getActiveTriggerCount(), + role_count: await Container.get(RoleRepository).countUsersByRole(), source_control_set_up: Container.get(SourceControlPreferencesService).isSourceControlSetup(), branchName: sourceControlPreferences.branchName, read_only_instance: sourceControlPreferences.branchReadOnly, diff --git a/packages/cli/test/integration/role.repository.test.ts b/packages/cli/test/integration/role.repository.test.ts new file mode 100644 index 0000000000..04e645928d --- /dev/null +++ b/packages/cli/test/integration/role.repository.test.ts @@ -0,0 +1,37 @@ +import { createAdmin, createMember, createOwner } from './shared/db/users'; +import * as testDb from './shared/testDb'; +import { RoleRepository } from '@/databases/repositories/role.repository'; +import Container from 'typedi'; + +describe('RoleRepository', () => { + let roleRepository: RoleRepository; + + beforeAll(async () => { + await testDb.init(); + + roleRepository = Container.get(RoleRepository); + + await testDb.truncate(['User']); + }); + + afterAll(async () => { + await testDb.terminate(); + }); + + describe('countUsersByRole()', () => { + test('should return the number of users in each role', async () => { + await Promise.all([ + createOwner(), + createAdmin(), + createAdmin(), + createMember(), + createMember(), + createMember(), + ]); + + const usersByRole = await roleRepository.countUsersByRole(); + + expect(usersByRole).toStrictEqual({ admin: 2, member: 3, owner: 1 }); + }); + }); +}); diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index fbcf70868a..d2a9b8fa42 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -1772,6 +1772,7 @@ export interface ExternalSecretsProviderWithProperties extends ExternalSecretsPr } export type CloudUpdateLinkSourceType = + | 'advanced-permissions' | 'canvas-nav' | 'custom-data-filter' | 'workflow_sharing' diff --git a/packages/editor-ui/src/components/InviteUsersModal.vue b/packages/editor-ui/src/components/InviteUsersModal.vue index 3145a91d1b..d8844697e9 100644 --- a/packages/editor-ui/src/components/InviteUsersModal.vue +++ b/packages/editor-ui/src/components/InviteUsersModal.vue @@ -333,7 +333,7 @@ export default defineComponent({ } }, goToUpgradeAdvancedPermissions() { - void this.uiStore.goToUpgrade('settings-users', 'upgrade-advanced-permissions'); + void this.uiStore.goToUpgrade('advanced-permissions', 'upgrade-advanced-permissions'); }, }, });