1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-20 00:08:02 +01:00

feat: feature lifecycle metrics from event bus (#6789)

This commit is contained in:
Mateusz Kwasniewski 2024-04-05 15:34:08 +02:00 committed by GitHub
parent 28a3a064b9
commit e868c3291f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 6 deletions

View File

@ -7,20 +7,23 @@ import {
type IUnleashConfig, type IUnleashConfig,
} from '../../types'; } from '../../types';
import { createFakeFeatureLifecycleService } from './createFeatureLifecycle'; import { createFakeFeatureLifecycleService } from './createFeatureLifecycle';
import EventEmitter from 'events';
function ms(timeMs) { function ms(timeMs) {
return new Promise((resolve) => setTimeout(resolve, timeMs)); return new Promise((resolve) => setTimeout(resolve, timeMs));
} }
test('can insert and read lifecycle stages', async () => { test('can insert and read lifecycle stages', async () => {
const eventBus = new EventEmitter();
const { featureLifecycleService, eventStore, environmentStore } = const { featureLifecycleService, eventStore, environmentStore } =
createFakeFeatureLifecycleService({ createFakeFeatureLifecycleService({
flagResolver: { isEnabled: () => true }, flagResolver: { isEnabled: () => true },
eventBus,
} as unknown as IUnleashConfig); } as unknown as IUnleashConfig);
const featureName = 'testFeature'; const featureName = 'testFeature';
async function emitMetricsEvent(environment: string) { async function emitMetricsEvent(environment: string) {
await eventStore.emit(CLIENT_METRICS, { featureName, environment }); await eventBus.emit(CLIENT_METRICS, { featureName, environment });
await ms(1); await ms(1);
} }
@ -68,9 +71,11 @@ test('can insert and read lifecycle stages', async () => {
}); });
test('ignores lifecycle state updates when flag disabled', async () => { test('ignores lifecycle state updates when flag disabled', async () => {
const eventBus = new EventEmitter();
const { featureLifecycleService, eventStore, environmentStore } = const { featureLifecycleService, eventStore, environmentStore } =
createFakeFeatureLifecycleService({ createFakeFeatureLifecycleService({
flagResolver: { isEnabled: () => false }, flagResolver: { isEnabled: () => false },
eventBus,
} as unknown as IUnleashConfig); } as unknown as IUnleashConfig);
const featureName = 'testFeature'; const featureName = 'testFeature';
@ -82,7 +87,7 @@ test('ignores lifecycle state updates when flag disabled', async () => {
await eventStore.emit(FEATURE_CREATED, { featureName }); await eventStore.emit(FEATURE_CREATED, { featureName });
await eventStore.emit(FEATURE_COMPLETED, { featureName }); await eventStore.emit(FEATURE_COMPLETED, { featureName });
await eventStore.emit(CLIENT_METRICS, { await eventBus.emit(CLIENT_METRICS, {
featureName, featureName,
environment: 'development', environment: 'development',
}); });

View File

@ -12,6 +12,7 @@ import type {
FeatureLifecycleView, FeatureLifecycleView,
IFeatureLifecycleStore, IFeatureLifecycleStore,
} from './feature-lifecycle-store-type'; } from './feature-lifecycle-store-type';
import type EventEmitter from 'events';
export class FeatureLifecycleService { export class FeatureLifecycleService {
private eventStore: IEventStore; private eventStore: IEventStore;
@ -22,6 +23,8 @@ export class FeatureLifecycleService {
private flagResolver: IFlagResolver; private flagResolver: IFlagResolver;
private eventBus: EventEmitter;
constructor( constructor(
{ {
eventStore, eventStore,
@ -32,12 +35,16 @@ export class FeatureLifecycleService {
environmentStore: IEnvironmentStore; environmentStore: IEnvironmentStore;
featureLifecycleStore: IFeatureLifecycleStore; featureLifecycleStore: IFeatureLifecycleStore;
}, },
{ flagResolver }: Pick<IUnleashConfig, 'flagResolver'>, {
flagResolver,
eventBus,
}: Pick<IUnleashConfig, 'flagResolver' | 'eventBus'>,
) { ) {
this.eventStore = eventStore; this.eventStore = eventStore;
this.featureLifecycleStore = featureLifecycleStore; this.featureLifecycleStore = featureLifecycleStore;
this.environmentStore = environmentStore; this.environmentStore = environmentStore;
this.flagResolver = flagResolver; this.flagResolver = flagResolver;
this.eventBus = eventBus;
} }
private async checkEnabled(fn: () => Promise<void>) { private async checkEnabled(fn: () => Promise<void>) {
@ -53,7 +60,7 @@ export class FeatureLifecycleService {
this.featureInitialized(event.featureName), this.featureInitialized(event.featureName),
); );
}); });
this.eventStore.on(CLIENT_METRICS, async (event) => { this.eventBus.on(CLIENT_METRICS, async (event) => {
await this.checkEnabled(() => await this.checkEnabled(() =>
this.featureReceivedMetrics( this.featureReceivedMetrics(
event.featureName, event.featureName,

View File

@ -4,9 +4,15 @@ import {
setupAppWithAuth, setupAppWithAuth,
} from '../../../test/e2e/helpers/test-helper'; } from '../../../test/e2e/helpers/test-helper';
import getLogger from '../../../test/fixtures/no-logger'; import getLogger from '../../../test/fixtures/no-logger';
import {
FEATURE_ARCHIVED,
FEATURE_CREATED,
type IEventStore,
} from '../../types';
let app: IUnleashTest; let app: IUnleashTest;
let db: ITestDb; let db: ITestDb;
let eventStore: IEventStore;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('feature_lifecycle', getLogger); db = await dbInit('feature_lifecycle', getLogger);
@ -21,6 +27,7 @@ beforeAll(async () => {
}, },
db.rawDatabase, db.rawDatabase,
); );
eventStore = db.stores.eventStore;
await app.request await app.request
.post(`/auth/demo/login`) .post(`/auth/demo/login`)
@ -43,10 +50,18 @@ const getFeatureLifecycle = async (featureName: string, expectedCode = 200) => {
.expect(expectedCode); .expect(expectedCode);
}; };
function ms(timeMs) {
return new Promise((resolve) => setTimeout(resolve, timeMs));
}
test('should return lifecycle stages', async () => { test('should return lifecycle stages', async () => {
await app.createFeature('my_feature_a'); await eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' });
await eventStore.emit(FEATURE_ARCHIVED, { featureName: 'my_feature_a' });
const { body } = await getFeatureLifecycle('my_feature_a'); const { body } = await getFeatureLifecycle('my_feature_a');
expect(body).toEqual([]); expect(body).toEqual([
{ stage: 'initial', enteredStageAt: expect.any(String) },
{ stage: 'archived', enteredStageAt: expect.any(String) },
]);
}); });