mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
feat: add prom metric for total custom root roles (#4435)
https://linear.app/unleash/issue/2-1293/label-our-metrics-about-roles-to-include-also-if-the-role-is-a-root Adds a Prometheus metric for total custom root roles. Also adds it to the instance telemetry collection. Q: Should we use a `labeledRoles` kind of metric instead, similar to what we're doing for `clientApps` and their ranges?
This commit is contained in:
parent
18b5161ade
commit
555b27a653
@ -42,6 +42,8 @@ const featureCollectionDetails = {
|
||||
'Context Fields': 'The number of custom context fields in use',
|
||||
Groups: 'The number of groups present in your instance',
|
||||
Roles: 'The number of custom roles defined in your instance',
|
||||
'Custom Root Roles':
|
||||
'The number of custom root roles defined in your instance',
|
||||
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',
|
||||
|
@ -9,6 +9,8 @@ import {
|
||||
} from 'lib/types/stores/role-store';
|
||||
import { IRole, IUserRole } from 'lib/types/stores/access-store';
|
||||
import { Db } from './db';
|
||||
import { PROJECT_ROLE_TYPES, ROOT_ROLE_TYPES } from '../util';
|
||||
import { RoleSchema } from 'lib/openapi';
|
||||
|
||||
const T = {
|
||||
ROLE_USER: 'role_user',
|
||||
@ -54,6 +56,14 @@ export default class RoleStore implements IRoleStore {
|
||||
.then((res) => Number(res[0].count));
|
||||
}
|
||||
|
||||
async filteredCount(filter: Partial<RoleSchema>): Promise<number> {
|
||||
return this.db
|
||||
.from(T.ROLES)
|
||||
.count('*')
|
||||
.where(filter)
|
||||
.then((res) => Number(res[0].count));
|
||||
}
|
||||
|
||||
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
||||
const row = await this.db(T.ROLES)
|
||||
.insert({
|
||||
@ -144,8 +154,7 @@ export default class RoleStore implements IRoleStore {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES)
|
||||
.where('type', 'custom')
|
||||
.orWhere('type', 'project');
|
||||
.whereIn('type', PROJECT_ROLE_TYPES);
|
||||
}
|
||||
|
||||
async getRolesForProject(projectId: string): Promise<IRole[]> {
|
||||
@ -160,7 +169,7 @@ export default class RoleStore implements IRoleStore {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES)
|
||||
.whereIn('type', ['root', 'root-custom']);
|
||||
.whereIn('type', ROOT_ROLE_TYPES);
|
||||
}
|
||||
|
||||
async removeRolesForProject(projectId: string): Promise<void> {
|
||||
@ -177,7 +186,7 @@ export default class RoleStore implements IRoleStore {
|
||||
.distinctOn('user_id')
|
||||
.from(`${T.ROLES} AS r`)
|
||||
.leftJoin(`${T.ROLE_USER} AS ru`, 'r.id', 'ru.role_id')
|
||||
.whereIn('r.type', ['root', 'root-custom']);
|
||||
.whereIn('r.type', ROOT_ROLE_TYPES);
|
||||
|
||||
return rows.map((row) => ({
|
||||
roleId: Number(row.id),
|
||||
|
@ -122,7 +122,7 @@ test('should collect metrics for feature toggle size', async () => {
|
||||
expect(metrics).toMatch(/feature_toggles_total\{version="(.*)"\} 0/);
|
||||
});
|
||||
|
||||
test('should collect metrics for feature toggle size', async () => {
|
||||
test('should collect metrics for total client apps', async () => {
|
||||
await new Promise((done) => {
|
||||
setTimeout(done, 10);
|
||||
});
|
||||
|
@ -104,6 +104,11 @@ export default class MetricsMonitor {
|
||||
help: 'Number of roles',
|
||||
});
|
||||
|
||||
const customRootRolesTotal = new client.Gauge({
|
||||
name: 'custom_root_roles_total',
|
||||
help: 'Number of custom root roles',
|
||||
});
|
||||
|
||||
const segmentsTotal = new client.Gauge({
|
||||
name: 'segments_total',
|
||||
help: 'Number of segments',
|
||||
@ -169,6 +174,9 @@ export default class MetricsMonitor {
|
||||
rolesTotal.reset();
|
||||
rolesTotal.set(stats.roles);
|
||||
|
||||
customRootRolesTotal.reset();
|
||||
customRootRolesTotal.set(stats.customRootRoles);
|
||||
|
||||
segmentsTotal.reset();
|
||||
segmentsTotal.set(stats.segments);
|
||||
|
||||
|
@ -99,6 +99,7 @@ class InstanceAdminController extends Controller {
|
||||
instanceId: 'ed3861ae-78f9-4e8c-8e57-b57efc15f82b',
|
||||
projects: 1,
|
||||
roles: 5,
|
||||
customRootRoles: 1,
|
||||
segments: 2,
|
||||
strategies: 8,
|
||||
sum: 'some-sha256-hash',
|
||||
|
@ -18,6 +18,7 @@ import { IRoleStore } from '../types/stores/role-store';
|
||||
import VersionService from './version-service';
|
||||
import { ISettingStore } from '../types/stores/settings-store';
|
||||
import { FEATURES_EXPORTED, FEATURES_IMPORTED } from '../types';
|
||||
import { CUSTOM_ROOT_ROLE_TYPE } from '../util';
|
||||
|
||||
export type TimeRange = 'allTime' | '30d' | '7d';
|
||||
|
||||
@ -31,6 +32,7 @@ export interface InstanceStats {
|
||||
projects: number;
|
||||
contextFields: number;
|
||||
roles: number;
|
||||
customRootRoles: number;
|
||||
featureExports: number;
|
||||
featureImports: number;
|
||||
groups: number;
|
||||
@ -177,6 +179,7 @@ export class InstanceStatsService {
|
||||
contextFields,
|
||||
groups,
|
||||
roles,
|
||||
customRootRoles,
|
||||
environments,
|
||||
segments,
|
||||
strategies,
|
||||
@ -192,6 +195,7 @@ export class InstanceStatsService {
|
||||
this.contextFieldStore.count(),
|
||||
this.groupStore.count(),
|
||||
this.roleStore.count(),
|
||||
this.roleStore.filteredCount({ type: CUSTOM_ROOT_ROLE_TYPE }),
|
||||
this.environmentStore.count(),
|
||||
this.segmentStore.count(),
|
||||
this.strategyStore.count(),
|
||||
@ -212,6 +216,7 @@ export class InstanceStatsService {
|
||||
projects,
|
||||
contextFields,
|
||||
roles,
|
||||
customRootRoles,
|
||||
groups,
|
||||
environments,
|
||||
segments,
|
||||
|
@ -19,6 +19,7 @@ import { ISettingStore } from '../types/stores/settings-store';
|
||||
import { hoursToMilliseconds } from 'date-fns';
|
||||
import { IStrategyStore } from 'lib/types';
|
||||
import { FEATURES_EXPORTED, FEATURES_IMPORTED } from '../types';
|
||||
import { CUSTOM_ROOT_ROLE_TYPE } from '../util';
|
||||
|
||||
export interface IVersionInfo {
|
||||
oss: string;
|
||||
@ -46,6 +47,7 @@ export interface IFeatureUsageInfo {
|
||||
projects: number;
|
||||
contextFields: number;
|
||||
roles: number;
|
||||
customRootRoles: number;
|
||||
featureExports: number;
|
||||
featureImports: number;
|
||||
groups: number;
|
||||
@ -226,6 +228,7 @@ export default class VersionService {
|
||||
contextFields,
|
||||
groups,
|
||||
roles,
|
||||
customRootRoles,
|
||||
environments,
|
||||
segments,
|
||||
strategies,
|
||||
@ -242,6 +245,9 @@ export default class VersionService {
|
||||
this.contextFieldStore.count(),
|
||||
this.groupStore.count(),
|
||||
this.roleStore.count(),
|
||||
this.roleStore.filteredCount({
|
||||
type: CUSTOM_ROOT_ROLE_TYPE,
|
||||
}),
|
||||
this.environmentStore.count(),
|
||||
this.segmentStore.count(),
|
||||
this.strategyStore.count(),
|
||||
@ -262,6 +268,7 @@ export default class VersionService {
|
||||
contextFields,
|
||||
groups,
|
||||
roles,
|
||||
customRootRoles,
|
||||
environments,
|
||||
segments,
|
||||
strategies,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { RoleSchema } from 'lib/openapi';
|
||||
import { ICustomRole } from '../model';
|
||||
import { IRole, IUserRole } from './access-store';
|
||||
import { Store } from './store';
|
||||
@ -29,4 +30,5 @@ export interface IRoleStore extends Store<ICustomRole, number> {
|
||||
getRootRoleForAllUsers(): Promise<IUserRole[]>;
|
||||
nameInUse(name: string, existingId?: number): Promise<boolean>;
|
||||
count(): Promise<number>;
|
||||
filteredCount(filter: Partial<RoleSchema>): Promise<number>;
|
||||
}
|
||||
|
5
src/test/fixtures/fake-role-store.ts
vendored
5
src/test/fixtures/fake-role-store.ts
vendored
@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { RoleSchema } from 'lib/openapi';
|
||||
import { ICustomRole } from 'lib/types/model';
|
||||
import { IRole, IUserRole } from 'lib/types/stores/access-store';
|
||||
import {
|
||||
@ -12,6 +13,10 @@ export default class FakeRoleStore implements IRoleStore {
|
||||
return Promise.resolve(0);
|
||||
}
|
||||
|
||||
filteredCount(search: Partial<RoleSchema>): Promise<number> {
|
||||
return Promise.resolve(0);
|
||||
}
|
||||
|
||||
roles: ICustomRole[] = [];
|
||||
|
||||
getGroupRolesForProject(projectId: string): Promise<IRole[]> {
|
||||
|
Loading…
Reference in New Issue
Block a user