mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
feat: add instance stats to version check (#3835)
<!-- Thanks for creating a PR! To make it easier for reviewers and everyone else to understand what your changes relate to, please add some relevant content to the headings below. Feel free to ignore or delete sections that you don't think are relevant. Thank you! ❤️ --> ## About the changes <!-- Describe the changes introduced. What are they and why are they being introduced? Feel free to also add screenshots or steps to view the changes if they're visual. --> Adds feature usage info and custom strategy counters to the version check object. <!-- Does it close an issue? Multiple? --> Closes # <!-- (For internal contributors): Does it relate to an issue on public roadmap? --> <!-- Relates to [roadmap](https://github.com/orgs/Unleash/projects/10) item: # --> ### Important files <!-- PRs can contain a lot of changes, but not all changes are equally important. Where should a reviewer start looking to get an overview of the changes? Are any files particularly important? --> ## Discussion points <!-- Anything about the PR you'd like to discuss before it gets merged? Got any questions or doubts? -->
This commit is contained in:
parent
5f3e5729b9
commit
98d315e062
@ -76,6 +76,7 @@ exports[`should create default config 1`] = `
|
||||
"disableNotifications": false,
|
||||
"embedProxy": true,
|
||||
"embedProxyFrontend": true,
|
||||
"experimentalExtendedTelemetry": false,
|
||||
"featuresExportImport": true,
|
||||
"googleAuthEnabled": false,
|
||||
"groupRootRoles": false,
|
||||
@ -109,6 +110,7 @@ exports[`should create default config 1`] = `
|
||||
"disableNotifications": false,
|
||||
"embedProxy": true,
|
||||
"embedProxyFrontend": true,
|
||||
"experimentalExtendedTelemetry": false,
|
||||
"featuresExportImport": true,
|
||||
"googleAuthEnabled": false,
|
||||
"groupRootRoles": false,
|
||||
|
95
src/lib/db/feature-strategy-store.test.ts
Normal file
95
src/lib/db/feature-strategy-store.test.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import dbInit from '../../test/e2e/helpers/database-init';
|
||||
import getLogger from '../../test/fixtures/no-logger';
|
||||
import FeatureStrategiesStore from './feature-strategy-store';
|
||||
import FeatureToggleStore from './feature-toggle-store';
|
||||
import StrategyStore from './strategy-store';
|
||||
|
||||
let db;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('feature_strategy_store_serial', getLogger);
|
||||
getLogger.setMuteError(true);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (db) {
|
||||
await db.destroy();
|
||||
}
|
||||
getLogger.setMuteError(false);
|
||||
});
|
||||
|
||||
test('returns 0 if no custom strategies', async () => {
|
||||
// Arrange
|
||||
const featureStrategiesStore: FeatureStrategiesStore =
|
||||
db.stores.featureStrategiesStore;
|
||||
|
||||
// Act
|
||||
const inUseCount =
|
||||
await featureStrategiesStore.getCustomStrategiesInUseCount();
|
||||
|
||||
// Assert
|
||||
expect(inUseCount).toEqual(0);
|
||||
});
|
||||
|
||||
test('returns 0 if no custom strategies are in use', async () => {
|
||||
// Arrange
|
||||
const featureToggleStore: FeatureToggleStore = db.stores.featureToggleStore;
|
||||
const featureStrategiesStore: FeatureStrategiesStore =
|
||||
db.stores.featureStrategiesStore;
|
||||
const strategyStore: StrategyStore = db.stores.strategyStore;
|
||||
|
||||
featureToggleStore.create('default', {
|
||||
name: 'test-toggle-2',
|
||||
});
|
||||
|
||||
strategyStore.createStrategy({
|
||||
name: 'strategy-2',
|
||||
built_in: 0,
|
||||
parameters: [],
|
||||
description: '',
|
||||
createdAt: '2023-06-09T09:00:12.242Z',
|
||||
});
|
||||
|
||||
// Act
|
||||
const inUseCount =
|
||||
await featureStrategiesStore.getCustomStrategiesInUseCount();
|
||||
|
||||
// Assert
|
||||
expect(inUseCount).toEqual(0);
|
||||
});
|
||||
|
||||
test('counts custom strategies in use', async () => {
|
||||
// Arrange
|
||||
const featureToggleStore: FeatureToggleStore = db.stores.featureToggleStore;
|
||||
const featureStrategiesStore: FeatureStrategiesStore =
|
||||
db.stores.featureStrategiesStore;
|
||||
const strategyStore: StrategyStore = db.stores.strategyStore;
|
||||
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'test-toggle',
|
||||
});
|
||||
|
||||
await strategyStore.createStrategy({
|
||||
name: 'strategy-1',
|
||||
built_in: 0,
|
||||
parameters: [],
|
||||
description: '',
|
||||
createdAt: '2023-06-09T09:00:12.242Z',
|
||||
});
|
||||
|
||||
await featureStrategiesStore.createStrategyFeatureEnv({
|
||||
projectId: 'default',
|
||||
featureName: 'test-toggle',
|
||||
strategyName: 'strategy-1',
|
||||
environment: 'default',
|
||||
parameters: {},
|
||||
constraints: [],
|
||||
});
|
||||
|
||||
// Act
|
||||
const inUseCount =
|
||||
await featureStrategiesStore.getCustomStrategiesInUseCount();
|
||||
|
||||
// Assert
|
||||
expect(inUseCount).toEqual(1);
|
||||
});
|
@ -50,6 +50,7 @@ const T = {
|
||||
featureStrategies: 'feature_strategies',
|
||||
featureStrategySegment: 'feature_strategy_segment',
|
||||
featureEnvs: 'feature_environments',
|
||||
strategies: 'strategies',
|
||||
};
|
||||
|
||||
interface IFeatureStrategiesTable {
|
||||
@ -665,6 +666,27 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
prefixColumns(): string[] {
|
||||
return COLUMNS.map((c) => `${T.featureStrategies}.${c}`);
|
||||
}
|
||||
|
||||
async getCustomStrategiesInUseCount(): Promise<number> {
|
||||
const stopTimer = this.timer('getCustomStrategiesInUseCount');
|
||||
const notBuiltIn = '0';
|
||||
const columns = [
|
||||
this.db.raw('count(fes.strategy_name) as times_used'),
|
||||
'fes.strategy_name',
|
||||
];
|
||||
const rows = await this.db(`${T.strategies} as str`)
|
||||
.select(columns)
|
||||
.join(
|
||||
`${T.featureStrategies} as fes`,
|
||||
'fes.strategy_name',
|
||||
'str.name',
|
||||
)
|
||||
.where(`str.built_in`, '=', notBuiltIn)
|
||||
.groupBy('strategy_name');
|
||||
|
||||
stopTimer();
|
||||
return rows.length;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FeatureStrategiesStore;
|
||||
|
@ -3,6 +3,7 @@ import createStores from '../../test/fixtures/store';
|
||||
import version from '../util/version';
|
||||
import getLogger from '../../test/fixtures/no-logger';
|
||||
import VersionService from './version-service';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { randomId } from '../util/random-id';
|
||||
|
||||
beforeAll(() => {
|
||||
@ -13,10 +14,25 @@ afterAll(() => {
|
||||
nock.enableNetConnect();
|
||||
});
|
||||
|
||||
const getTestFlagResolver = (enabled: boolean) => {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
isEnabled: () => {
|
||||
return enabled;
|
||||
},
|
||||
getAll: () => {
|
||||
return {};
|
||||
},
|
||||
getVariant: () => {
|
||||
return { name: '', enabled: false };
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
test('yields current versions', async () => {
|
||||
const url = `https://${randomId()}.example.com`;
|
||||
const { settingStore } = createStores();
|
||||
await settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
const stores = createStores();
|
||||
await stores.settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
const latest = {
|
||||
oss: '5.0.0',
|
||||
enterprise: '5.0.0',
|
||||
@ -30,13 +46,11 @@ test('yields current versions', async () => {
|
||||
versions: latest,
|
||||
}),
|
||||
]);
|
||||
const service = new VersionService(
|
||||
{ settingStore },
|
||||
{
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
},
|
||||
);
|
||||
const service = new VersionService(stores, {
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
flagResolver: getTestFlagResolver(true),
|
||||
});
|
||||
await service.checkLatestVersion();
|
||||
const versionInfo = service.getVersionInfo();
|
||||
expect(scope.isDone()).toEqual(true);
|
||||
@ -48,9 +62,9 @@ test('yields current versions', async () => {
|
||||
|
||||
test('supports setting enterprise version as well', async () => {
|
||||
const url = `https://${randomId()}.example.com`;
|
||||
const { settingStore } = createStores();
|
||||
const stores = createStores();
|
||||
const enterpriseVersion = '3.7.0';
|
||||
await settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
await stores.settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
const latest = {
|
||||
oss: '4.0.0',
|
||||
enterprise: '4.0.0',
|
||||
@ -65,14 +79,12 @@ test('supports setting enterprise version as well', async () => {
|
||||
}),
|
||||
]);
|
||||
|
||||
const service = new VersionService(
|
||||
{ settingStore },
|
||||
{
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
enterpriseVersion,
|
||||
},
|
||||
);
|
||||
const service = new VersionService(stores, {
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
enterpriseVersion,
|
||||
flagResolver: getTestFlagResolver(true),
|
||||
});
|
||||
await service.checkLatestVersion();
|
||||
const versionInfo = service.getVersionInfo();
|
||||
expect(scope.isDone()).toEqual(true);
|
||||
@ -84,9 +96,9 @@ test('supports setting enterprise version as well', async () => {
|
||||
|
||||
test('if version check is not enabled should not make any calls', async () => {
|
||||
const url = `https://${randomId()}.example.com`;
|
||||
const { settingStore } = createStores();
|
||||
const stores = createStores();
|
||||
const enterpriseVersion = '3.7.0';
|
||||
await settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
await stores.settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
const latest = {
|
||||
oss: '4.0.0',
|
||||
enterprise: '4.0.0',
|
||||
@ -101,14 +113,12 @@ test('if version check is not enabled should not make any calls', async () => {
|
||||
}),
|
||||
]);
|
||||
|
||||
const service = new VersionService(
|
||||
{ settingStore },
|
||||
{
|
||||
getLogger,
|
||||
versionCheck: { url, enable: false },
|
||||
enterpriseVersion,
|
||||
},
|
||||
);
|
||||
const service = new VersionService(stores, {
|
||||
getLogger,
|
||||
versionCheck: { url, enable: false },
|
||||
enterpriseVersion,
|
||||
flagResolver: getTestFlagResolver(true),
|
||||
});
|
||||
await service.checkLatestVersion();
|
||||
const versionInfo = service.getVersionInfo();
|
||||
expect(scope.isDone()).toEqual(false);
|
||||
@ -118,3 +128,189 @@ test('if version check is not enabled should not make any calls', async () => {
|
||||
expect(versionInfo.latest.enterprise).toBeFalsy();
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
test('sets featureinfo', async () => {
|
||||
const url = `https://${randomId()}.example.com`;
|
||||
const stores = createStores();
|
||||
const enterpriseVersion = '4.0.0';
|
||||
await stores.settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
const latest = {
|
||||
oss: '4.0.0',
|
||||
enterprise: '4.0.0',
|
||||
};
|
||||
|
||||
const scope = nock(url)
|
||||
.post(
|
||||
'/',
|
||||
(body) =>
|
||||
body.featureInfo &&
|
||||
body.featureInfo.featureToggles === 0 &&
|
||||
body.featureInfo.environments === 0,
|
||||
)
|
||||
.reply(() => [
|
||||
200,
|
||||
JSON.stringify({
|
||||
latest: true,
|
||||
versions: latest,
|
||||
}),
|
||||
]);
|
||||
|
||||
const service = new VersionService(stores, {
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
enterpriseVersion,
|
||||
flagResolver: getTestFlagResolver(true),
|
||||
});
|
||||
await service.checkLatestVersion();
|
||||
expect(scope.isDone()).toEqual(true);
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
test('counts toggles', async () => {
|
||||
const url = `https://${randomId()}.example.com`;
|
||||
const stores = createStores();
|
||||
const enterpriseVersion = '4.0.0';
|
||||
await stores.settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
await stores.settingStore.insert('unleash.enterprise.auth.oidc', {
|
||||
enabled: true,
|
||||
});
|
||||
await stores.featureToggleStore.create('project', { name: uuidv4() });
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: uuidv4(),
|
||||
editable: true,
|
||||
});
|
||||
const latest = {
|
||||
oss: '4.0.0',
|
||||
enterprise: '4.0.0',
|
||||
};
|
||||
|
||||
const scope = nock(url)
|
||||
.post(
|
||||
'/',
|
||||
(body) =>
|
||||
body.featureInfo &&
|
||||
body.featureInfo.featureToggles === 1 &&
|
||||
body.featureInfo.environments === 0 &&
|
||||
body.featureInfo.customStrategies === 1 &&
|
||||
body.featureInfo.customStrategiesInUse === 3 &&
|
||||
body.featureInfo.OIDCenabled,
|
||||
)
|
||||
.reply(() => [
|
||||
200,
|
||||
JSON.stringify({
|
||||
latest: true,
|
||||
versions: latest,
|
||||
}),
|
||||
]);
|
||||
|
||||
const service = new VersionService(stores, {
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
enterpriseVersion,
|
||||
flagResolver: getTestFlagResolver(true),
|
||||
});
|
||||
await service.checkLatestVersion();
|
||||
expect(scope.isDone()).toEqual(true);
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
test('doesnt report featureinfo when flag off', async () => {
|
||||
const url = `https://${randomId()}.example.com`;
|
||||
const stores = createStores();
|
||||
const enterpriseVersion = '4.0.0';
|
||||
await stores.settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
await stores.settingStore.insert('unleash.enterprise.auth.oidc', {
|
||||
enabled: true,
|
||||
});
|
||||
await stores.featureToggleStore.create('project', { name: uuidv4() });
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: uuidv4(),
|
||||
editable: true,
|
||||
});
|
||||
const latest = {
|
||||
oss: '4.0.0',
|
||||
enterprise: '4.0.0',
|
||||
};
|
||||
|
||||
const scope = nock(url)
|
||||
.post('/', (body) => body.featureInfo === undefined)
|
||||
.reply(() => [
|
||||
200,
|
||||
JSON.stringify({
|
||||
latest: true,
|
||||
versions: latest,
|
||||
}),
|
||||
]);
|
||||
|
||||
const service = new VersionService(stores, {
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
enterpriseVersion,
|
||||
flagResolver: getTestFlagResolver(false),
|
||||
});
|
||||
await service.checkLatestVersion();
|
||||
expect(scope.isDone()).toEqual(true);
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
test('counts custom strategies', async () => {
|
||||
const url = `https://${randomId()}.example.com`;
|
||||
const stores = createStores();
|
||||
const enterpriseVersion = '4.0.0';
|
||||
const strategyName = uuidv4();
|
||||
const toggleName = uuidv4();
|
||||
await stores.settingStore.insert('instanceInfo', { id: '1234abc' });
|
||||
await stores.settingStore.insert('unleash.enterprise.auth.oidc', {
|
||||
enabled: true,
|
||||
});
|
||||
await stores.featureToggleStore.create('project', { name: toggleName });
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: strategyName,
|
||||
editable: true,
|
||||
});
|
||||
await stores.strategyStore.createStrategy({
|
||||
name: uuidv4(),
|
||||
editable: true,
|
||||
});
|
||||
await stores.featureStrategiesStore.createStrategyFeatureEnv({
|
||||
featureName: toggleName,
|
||||
projectId: 'project',
|
||||
environment: 'default',
|
||||
strategyName: strategyName,
|
||||
parameters: {},
|
||||
constraints: [],
|
||||
});
|
||||
const latest = {
|
||||
oss: '4.0.0',
|
||||
enterprise: '4.0.0',
|
||||
};
|
||||
|
||||
const scope = nock(url)
|
||||
.post(
|
||||
'/',
|
||||
(body) =>
|
||||
body.featureInfo &&
|
||||
body.featureInfo.featureToggles === 1 &&
|
||||
body.featureInfo.environments === 0 &&
|
||||
body.featureInfo.customStrategies === 2 &&
|
||||
body.featureInfo.customStrategiesInUse === 3 &&
|
||||
body.featureInfo.OIDCenabled,
|
||||
)
|
||||
.reply(() => [
|
||||
200,
|
||||
JSON.stringify({
|
||||
latest: true,
|
||||
versions: latest,
|
||||
}),
|
||||
]);
|
||||
|
||||
const service = new VersionService(stores, {
|
||||
getLogger,
|
||||
versionCheck: { url, enable: true },
|
||||
enterpriseVersion,
|
||||
flagResolver: getTestFlagResolver(true),
|
||||
});
|
||||
await service.checkLatestVersion();
|
||||
expect(scope.isDone()).toEqual(true);
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
@ -1,10 +1,25 @@
|
||||
import fetch from 'make-fetch-happen';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import {
|
||||
IContextFieldStore,
|
||||
IEnvironmentStore,
|
||||
IEventStore,
|
||||
IFeatureStrategiesStore,
|
||||
IFeatureToggleStore,
|
||||
IGroupStore,
|
||||
IProjectStore,
|
||||
IRoleStore,
|
||||
ISegmentStore,
|
||||
IUnleashStores,
|
||||
IUserStore,
|
||||
} from '../types/stores';
|
||||
import { IUnleashConfig } from '../types/option';
|
||||
import version from '../util/version';
|
||||
import { Logger } from '../logger';
|
||||
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 { IFlagResolver } from '../types';
|
||||
|
||||
export interface IVersionInfo {
|
||||
oss: string;
|
||||
@ -23,11 +38,54 @@ export interface IVersionResponse {
|
||||
latest: boolean;
|
||||
}
|
||||
|
||||
export interface IFeatureUsageInfo {
|
||||
instanceId: string;
|
||||
versionOSS: string;
|
||||
versionEnterprise?: string;
|
||||
users: number;
|
||||
featureToggles: number;
|
||||
projects: number;
|
||||
contextFields: number;
|
||||
roles: number;
|
||||
featureExports: number;
|
||||
featureImports: number;
|
||||
groups: number;
|
||||
environments: number;
|
||||
segments: number;
|
||||
strategies: number;
|
||||
SAMLenabled: boolean;
|
||||
OIDCenabled: boolean;
|
||||
customStrategies: number;
|
||||
customStrategiesInUse: number;
|
||||
}
|
||||
|
||||
export default class VersionService {
|
||||
private logger: Logger;
|
||||
|
||||
private settingStore: ISettingStore;
|
||||
|
||||
private strategyStore: IStrategyStore;
|
||||
|
||||
private userStore: IUserStore;
|
||||
|
||||
private featureToggleStore: IFeatureToggleStore;
|
||||
|
||||
private projectStore: IProjectStore;
|
||||
|
||||
private environmentStore: IEnvironmentStore;
|
||||
|
||||
private contextFieldStore: IContextFieldStore;
|
||||
|
||||
private groupStore: IGroupStore;
|
||||
|
||||
private roleStore: IRoleStore;
|
||||
|
||||
private segmentStore: ISegmentStore;
|
||||
|
||||
private eventStore: IEventStore;
|
||||
|
||||
private featureStrategiesStore: IFeatureStrategiesStore;
|
||||
|
||||
private current: IVersionInfo;
|
||||
|
||||
private latest?: IVersionInfo;
|
||||
@ -42,19 +100,60 @@ export default class VersionService {
|
||||
|
||||
private timer: NodeJS.Timeout;
|
||||
|
||||
private flagResolver: IFlagResolver;
|
||||
|
||||
constructor(
|
||||
{ settingStore }: Pick<IUnleashStores, 'settingStore'>,
|
||||
{
|
||||
settingStore,
|
||||
strategyStore,
|
||||
userStore,
|
||||
featureToggleStore,
|
||||
projectStore,
|
||||
environmentStore,
|
||||
contextFieldStore,
|
||||
groupStore,
|
||||
roleStore,
|
||||
segmentStore,
|
||||
eventStore,
|
||||
featureStrategiesStore,
|
||||
}: Pick<
|
||||
IUnleashStores,
|
||||
| 'settingStore'
|
||||
| 'strategyStore'
|
||||
| 'userStore'
|
||||
| 'featureToggleStore'
|
||||
| 'projectStore'
|
||||
| 'environmentStore'
|
||||
| 'contextFieldStore'
|
||||
| 'groupStore'
|
||||
| 'roleStore'
|
||||
| 'segmentStore'
|
||||
| 'eventStore'
|
||||
| 'featureStrategiesStore'
|
||||
>,
|
||||
{
|
||||
getLogger,
|
||||
versionCheck,
|
||||
enterpriseVersion,
|
||||
flagResolver,
|
||||
}: Pick<
|
||||
IUnleashConfig,
|
||||
'getLogger' | 'versionCheck' | 'enterpriseVersion'
|
||||
'getLogger' | 'versionCheck' | 'enterpriseVersion' | 'flagResolver'
|
||||
>,
|
||||
) {
|
||||
this.logger = getLogger('lib/services/version-service.js');
|
||||
this.settingStore = settingStore;
|
||||
this.strategyStore = strategyStore;
|
||||
this.userStore = userStore;
|
||||
this.featureToggleStore = featureToggleStore;
|
||||
this.projectStore = projectStore;
|
||||
this.environmentStore = environmentStore;
|
||||
this.contextFieldStore = contextFieldStore;
|
||||
this.groupStore = groupStore;
|
||||
this.roleStore = roleStore;
|
||||
this.segmentStore = segmentStore;
|
||||
this.eventStore = eventStore;
|
||||
this.featureStrategiesStore = featureStrategiesStore;
|
||||
this.current = {
|
||||
oss: version,
|
||||
enterprise: enterpriseVersion || '',
|
||||
@ -62,6 +161,7 @@ export default class VersionService {
|
||||
this.enabled = versionCheck.enable;
|
||||
this.versionCheckUrl = versionCheck.url;
|
||||
this.isLatest = true;
|
||||
this.flagResolver = flagResolver;
|
||||
process.nextTick(() => this.setup());
|
||||
}
|
||||
|
||||
@ -87,12 +187,20 @@ export default class VersionService {
|
||||
async checkLatestVersion(): Promise<void> {
|
||||
if (this.enabled) {
|
||||
try {
|
||||
const versionPayload: any = {
|
||||
versions: this.current,
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
|
||||
if (
|
||||
this.flagResolver.isEnabled('experimentalExtendedTelemetry')
|
||||
) {
|
||||
const featureInfo = await this.getFeatureUsageInfo();
|
||||
versionPayload.featureInfo = featureInfo;
|
||||
}
|
||||
const res = await fetch(this.versionCheckUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
versions: this.current,
|
||||
instanceId: this.instanceId,
|
||||
}),
|
||||
body: JSON.stringify(versionPayload),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (res.ok) {
|
||||
@ -113,6 +221,82 @@ export default class VersionService {
|
||||
}
|
||||
}
|
||||
|
||||
async getFeatureUsageInfo(): Promise<IFeatureUsageInfo> {
|
||||
const [
|
||||
featureToggles,
|
||||
users,
|
||||
projects,
|
||||
contextFields,
|
||||
groups,
|
||||
roles,
|
||||
environments,
|
||||
segments,
|
||||
strategies,
|
||||
SAMLenabled,
|
||||
OIDCenabled,
|
||||
featureExports,
|
||||
featureImports,
|
||||
] = await Promise.all([
|
||||
this.featureToggleStore.count({
|
||||
archived: false,
|
||||
}),
|
||||
this.userStore.count(),
|
||||
this.projectStore.count(),
|
||||
this.contextFieldStore.count(),
|
||||
this.groupStore.count(),
|
||||
this.roleStore.count(),
|
||||
this.environmentStore.count(),
|
||||
this.segmentStore.count(),
|
||||
this.strategyStore.count(),
|
||||
this.hasSAML(),
|
||||
this.hasOIDC(),
|
||||
this.eventStore.filteredCount({ type: FEATURES_EXPORTED }),
|
||||
this.eventStore.filteredCount({ type: FEATURES_IMPORTED }),
|
||||
]);
|
||||
const versionInfo = this.getVersionInfo();
|
||||
const customStrategies =
|
||||
await this.strategyStore.getEditableStrategies();
|
||||
const customStrategiesInUse =
|
||||
await this.featureStrategiesStore.getCustomStrategiesInUseCount();
|
||||
const featureInfo = {
|
||||
featureToggles,
|
||||
users,
|
||||
projects,
|
||||
contextFields,
|
||||
groups,
|
||||
roles,
|
||||
environments,
|
||||
segments,
|
||||
strategies,
|
||||
SAMLenabled,
|
||||
OIDCenabled,
|
||||
featureExports,
|
||||
featureImports,
|
||||
customStrategies: customStrategies.length,
|
||||
customStrategiesInUse: customStrategiesInUse,
|
||||
instanceId: versionInfo.instanceId,
|
||||
versionOSS: versionInfo.current.oss,
|
||||
versionEnterprise: versionInfo.current.enterprise,
|
||||
};
|
||||
return featureInfo;
|
||||
}
|
||||
|
||||
async hasOIDC(): Promise<boolean> {
|
||||
const settings = await this.settingStore.get(
|
||||
'unleash.enterprise.auth.oidc',
|
||||
);
|
||||
|
||||
return settings?.enabled || false;
|
||||
}
|
||||
|
||||
async hasSAML(): Promise<boolean> {
|
||||
const settings = await this.settingStore.get(
|
||||
'unleash.enterprise.auth.saml',
|
||||
);
|
||||
|
||||
return settings?.enabled || false;
|
||||
}
|
||||
|
||||
getVersionInfo(): IVersionHolder {
|
||||
return {
|
||||
current: this.current,
|
||||
|
@ -22,6 +22,7 @@ export type IFlagKey =
|
||||
| 'googleAuthEnabled'
|
||||
| 'variantMetrics'
|
||||
| 'disableBulkToggle'
|
||||
| 'experimentalExtendedTelemetry'
|
||||
| 'segmentContextFieldUsage'
|
||||
| 'disableNotifications'
|
||||
| 'advancedPlayground';
|
||||
@ -97,6 +98,10 @@ const flags: IFlags = {
|
||||
process.env.UNLEASH_VARIANT_METRICS,
|
||||
false,
|
||||
),
|
||||
experimentalExtendedTelemetry: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_EXTENDED_TELEMETRY,
|
||||
false,
|
||||
),
|
||||
disableBulkToggle: parseEnvVarBoolean(
|
||||
process.env.DISABLE_BULK_TOGGLE,
|
||||
false,
|
||||
|
@ -66,4 +66,5 @@ export interface IFeatureStrategiesStore
|
||||
features: string[],
|
||||
environment?: string,
|
||||
): Promise<IFeatureStrategy[]>;
|
||||
getCustomStrategiesInUseCount(): Promise<number>;
|
||||
}
|
||||
|
@ -334,6 +334,10 @@ export default class FakeFeatureStrategiesStore
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
getCustomStrategiesInUseCount(): Promise<number> {
|
||||
return Promise.resolve(3);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FakeFeatureStrategiesStore;
|
||||
|
Loading…
Reference in New Issue
Block a user