mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
feat: count created feature links with impact metrics
This commit is contained in:
parent
4289a1c105
commit
783270599b
@ -6,13 +6,21 @@ import {
|
|||||||
NotFoundError,
|
NotFoundError,
|
||||||
OperationDeniedError,
|
OperationDeniedError,
|
||||||
} from '../../error/index.js';
|
} from '../../error/index.js';
|
||||||
|
import { fakeImpactMetricsResolver } from '../../../test/fixtures/fake-impact-metrics.js';
|
||||||
|
|
||||||
test('create, update and delete feature link', async () => {
|
test('create, update and delete feature link', async () => {
|
||||||
|
const flagResolver = { impactMetrics: fakeImpactMetricsResolver() };
|
||||||
const { featureLinkStore, featureLinkService } =
|
const { featureLinkStore, featureLinkService } =
|
||||||
createFakeFeatureLinkService({
|
createFakeFeatureLinkService({
|
||||||
getLogger,
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
} as unknown as IUnleashConfig);
|
} as unknown as IUnleashConfig);
|
||||||
|
|
||||||
|
flagResolver.impactMetrics.defineCounter(
|
||||||
|
'feature_link_count',
|
||||||
|
'Count of feature links',
|
||||||
|
);
|
||||||
|
|
||||||
const link = await featureLinkService.createLink(
|
const link = await featureLinkService.createLink(
|
||||||
'default',
|
'default',
|
||||||
{
|
{
|
||||||
@ -29,6 +37,10 @@ test('create, update and delete feature link', async () => {
|
|||||||
domain: 'example',
|
domain: 'example',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
flagResolver.impactMetrics.counters.get('feature_link_count')!.value,
|
||||||
|
).toBe(1);
|
||||||
|
|
||||||
const newLink = await featureLinkService.updateLink(
|
const newLink = await featureLinkService.updateLink(
|
||||||
{ projectId: 'default', linkId: link.id },
|
{ projectId: 'default', linkId: link.id },
|
||||||
{
|
{
|
||||||
@ -53,8 +65,10 @@ test('create, update and delete feature link', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('cannot delete/update non existent link', async () => {
|
test('cannot delete/update non existent link', async () => {
|
||||||
|
const flagResolver = { impactMetrics: fakeImpactMetricsResolver() };
|
||||||
const { featureLinkService } = createFakeFeatureLinkService({
|
const { featureLinkService } = createFakeFeatureLinkService({
|
||||||
getLogger,
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
} as unknown as IUnleashConfig);
|
} as unknown as IUnleashConfig);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@ -77,8 +91,10 @@ test('cannot delete/update non existent link', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('cannot create/update invalid link', async () => {
|
test('cannot create/update invalid link', async () => {
|
||||||
|
const flagResolver = { impactMetrics: fakeImpactMetricsResolver() };
|
||||||
const { featureLinkService } = createFakeFeatureLinkService({
|
const { featureLinkService } = createFakeFeatureLinkService({
|
||||||
getLogger,
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
} as unknown as IUnleashConfig);
|
} as unknown as IUnleashConfig);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@ -107,8 +123,10 @@ test('cannot create/update invalid link', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('cannot exceed allowed link count', async () => {
|
test('cannot exceed allowed link count', async () => {
|
||||||
|
const flagResolver = { impactMetrics: fakeImpactMetricsResolver() };
|
||||||
const { featureLinkService } = createFakeFeatureLinkService({
|
const { featureLinkService } = createFakeFeatureLinkService({
|
||||||
getLogger,
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
} as unknown as IUnleashConfig);
|
} as unknown as IUnleashConfig);
|
||||||
|
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
FeatureLinkRemovedEvent,
|
FeatureLinkRemovedEvent,
|
||||||
FeatureLinkUpdatedEvent,
|
FeatureLinkUpdatedEvent,
|
||||||
type IAuditUser,
|
type IAuditUser,
|
||||||
|
type IFlagResolver,
|
||||||
type IUnleashConfig,
|
type IUnleashConfig,
|
||||||
} from '../../types/index.js';
|
} from '../../types/index.js';
|
||||||
import type {
|
import type {
|
||||||
@ -18,6 +19,7 @@ import {
|
|||||||
} from '../../error/index.js';
|
} from '../../error/index.js';
|
||||||
import normalizeUrl from 'normalize-url';
|
import normalizeUrl from 'normalize-url';
|
||||||
import { parse } from 'tldts';
|
import { parse } from 'tldts';
|
||||||
|
import { FEAUTRE_LINK_COUNT } from '../metrics/impact/define-impact-metrics.js';
|
||||||
|
|
||||||
interface IFeatureLinkStoreObj {
|
interface IFeatureLinkStoreObj {
|
||||||
featureLinkStore: IFeatureLinkStore;
|
featureLinkStore: IFeatureLinkStore;
|
||||||
@ -27,15 +29,20 @@ export default class FeatureLinkService {
|
|||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
private featureLinkStore: IFeatureLinkStore;
|
private featureLinkStore: IFeatureLinkStore;
|
||||||
private eventService: EventService;
|
private eventService: EventService;
|
||||||
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
stores: IFeatureLinkStoreObj,
|
stores: IFeatureLinkStoreObj,
|
||||||
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
{
|
||||||
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
|
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
|
||||||
eventService: EventService,
|
eventService: EventService,
|
||||||
) {
|
) {
|
||||||
this.logger = getLogger('feature-links/feature-link-service.ts');
|
this.logger = getLogger('feature-links/feature-link-service.ts');
|
||||||
this.featureLinkStore = stores.featureLinkStore;
|
this.featureLinkStore = stores.featureLinkStore;
|
||||||
this.eventService = eventService;
|
this.eventService = eventService;
|
||||||
|
this.flagResolver = flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<IFeatureLink[]> {
|
async getAll(): Promise<IFeatureLink[]> {
|
||||||
@ -72,6 +79,8 @@ export default class FeatureLinkService {
|
|||||||
domain: domainWithoutSuffix,
|
domain: domainWithoutSuffix,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.flagResolver.impactMetrics?.incrementCounter(FEAUTRE_LINK_COUNT);
|
||||||
|
|
||||||
await this.eventService.storeEvent(
|
await this.eventService.storeEvent(
|
||||||
new FeatureLinkAddedEvent({
|
new FeatureLinkAddedEvent({
|
||||||
featureName: newLink.featureName,
|
featureName: newLink.featureName,
|
||||||
|
@ -19,17 +19,7 @@ let featureLinkReadModel: IFeatureLinksReadModel;
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_link', getLogger);
|
db = await dbInit('feature_link', getLogger);
|
||||||
app = await setupAppWithAuth(
|
app = await setupAppWithAuth(db.stores, {}, db.rawDatabase);
|
||||||
db.stores,
|
|
||||||
{
|
|
||||||
experimental: {
|
|
||||||
flags: {
|
|
||||||
featureLinks: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
db.rawDatabase,
|
|
||||||
);
|
|
||||||
eventStore = db.stores.eventStore;
|
eventStore = db.stores.eventStore;
|
||||||
featureLinkStore = db.stores.featureLinkStore;
|
featureLinkStore = db.stores.featureLinkStore;
|
||||||
featureLinkReadModel = new FeatureLinksReadModel(
|
featureLinkReadModel = new FeatureLinksReadModel(
|
||||||
|
10
src/lib/features/metrics/impact/define-impact-metrics.ts
Normal file
10
src/lib/features/metrics/impact/define-impact-metrics.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { IFlagResolver } from '../../../types/index.js';
|
||||||
|
|
||||||
|
export const FEAUTRE_LINK_COUNT = 'feature_link_count';
|
||||||
|
|
||||||
|
export const defineImpactMetrics = (flagResolver: IFlagResolver) => {
|
||||||
|
flagResolver.impactMetrics?.defineCounter(
|
||||||
|
FEAUTRE_LINK_COUNT,
|
||||||
|
'Count of feature links',
|
||||||
|
);
|
||||||
|
};
|
@ -183,6 +183,7 @@ import { testDbPrefix } from '../test/e2e/helpers/database-init.js';
|
|||||||
import type { RequestHandler } from 'express';
|
import type { RequestHandler } from 'express';
|
||||||
import { UPDATE_REVISION } from './features/feature-toggle/configuration-revision-service.js';
|
import { UPDATE_REVISION } from './features/feature-toggle/configuration-revision-service.js';
|
||||||
import type { IFeatureUsageInfo } from './services/version-service.js';
|
import type { IFeatureUsageInfo } from './services/version-service.js';
|
||||||
|
import { defineImpactMetrics } from './features/metrics/impact/define-impact-metrics.js';
|
||||||
|
|
||||||
export async function initialServiceSetup(
|
export async function initialServiceSetup(
|
||||||
{ authentication }: Pick<IUnleashConfig, 'authentication'>,
|
{ authentication }: Pick<IUnleashConfig, 'authentication'>,
|
||||||
@ -232,6 +233,7 @@ export async function createApp(
|
|||||||
scheduleServices(services, config);
|
scheduleServices(services, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineImpactMetrics(config.flagResolver);
|
||||||
const metricsMonitor = fm.createMetricsMonitor();
|
const metricsMonitor = fm.createMetricsMonitor();
|
||||||
const unleashSession = fm.createSessionDb(config, db);
|
const unleashSession = fm.createSessionDb(config, db);
|
||||||
|
|
||||||
|
34
src/test/fixtures/fake-impact-metrics.ts
vendored
Normal file
34
src/test/fixtures/fake-impact-metrics.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export const fakeImpactMetricsResolver = () => ({
|
||||||
|
counters: new Map<string, { value: number; help: string }>(),
|
||||||
|
gauges: new Map<string, { value: number; help: string }>(),
|
||||||
|
|
||||||
|
defineCounter(name: string, help: string) {
|
||||||
|
this.counters.set(name, { value: 0, help });
|
||||||
|
},
|
||||||
|
|
||||||
|
defineGauge(name: string, help: string) {
|
||||||
|
this.gauges.set(name, { value: 0, help });
|
||||||
|
},
|
||||||
|
|
||||||
|
incrementCounter(name: string, value: number = 1) {
|
||||||
|
const counter = this.counters.get(name);
|
||||||
|
|
||||||
|
if (!counter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.value += value;
|
||||||
|
this.counters.set(name, counter);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateGauge(name: string, value: number) {
|
||||||
|
const gauge = this.gauges.get(name);
|
||||||
|
|
||||||
|
if (!gauge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gauge.value = value;
|
||||||
|
this.gauges.set(name, gauge);
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user