1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-24 17:51:14 +02:00
This commit is contained in:
Jaanus 2025-09-01 15:51:30 +03:00
parent 8ee9fa5574
commit 91d93cb247
No known key found for this signature in database

View File

@ -8,8 +8,10 @@ import {
FeatureCompletedEvent, FeatureCompletedEvent,
FeatureUncompletedEvent, FeatureUncompletedEvent,
type IAuditUser, type IAuditUser,
type IEnvironment,
type IEnvironmentStore, type IEnvironmentStore,
type IEventStore, type IEventStore,
type IFeatureEnvironment,
type IFeatureEnvironmentStore, type IFeatureEnvironmentStore,
type IFlagResolver, type IFlagResolver,
type IUnleashConfig, type IUnleashConfig,
@ -20,12 +22,12 @@ import type {
NewStage, NewStage,
} from './feature-lifecycle-store-type.js'; } from './feature-lifecycle-store-type.js';
import type EventEmitter from 'events'; import type EventEmitter from 'events';
import type { Logger } from '../../logger.js'; import type {Logger} from '../../logger.js';
import type EventService from '../events/event-service.js'; import type EventService from '../events/event-service.js';
import type { FeatureLifecycleCompletedSchema } from '../../openapi/index.js'; import type {FeatureLifecycleCompletedSchema} from '../../openapi/index.js';
import type { IClientMetricsEnv } from '../metrics/client-metrics/client-metrics-store-v2-type.js'; import type {IClientMetricsEnv} from '../metrics/client-metrics/client-metrics-store-v2-type.js';
import groupBy from 'lodash.groupby'; import groupBy from 'lodash.groupby';
import { STAGE_ENTERED } from '../../metric-events.js'; import {STAGE_ENTERED} from '../../metric-events.js';
export class FeatureLifecycleService { export class FeatureLifecycleService {
private eventStore: IEventStore; private eventStore: IEventStore;
@ -125,7 +127,7 @@ export class FeatureLifecycleService {
private async featureInitialized(feature: string) { private async featureInitialized(feature: string) {
const result = await this.featureLifecycleStore.insert([ const result = await this.featureLifecycleStore.insert([
{ feature, stage: 'initial' }, {feature, stage: 'initial'},
]); ]);
this.recordStagesEntered(result); this.recordStagesEntered(result);
} }
@ -135,14 +137,14 @@ export class FeatureLifecycleService {
stage: 'live' | 'pre-live', stage: 'live' | 'pre-live',
) { ) {
const newlyEnteredStages = await this.featureLifecycleStore.insert( const newlyEnteredStages = await this.featureLifecycleStore.insert(
features.map((feature) => ({ feature, stage })), features.map((feature) => ({feature, stage})),
); );
this.recordStagesEntered(newlyEnteredStages); this.recordStagesEntered(newlyEnteredStages);
} }
private recordStagesEntered(newlyEnteredStages: NewStage[]) { private recordStagesEntered(newlyEnteredStages: NewStage[]) {
newlyEnteredStages.forEach(({ stage, feature }) => { newlyEnteredStages.forEach(({stage, feature}) => {
this.eventBus.emit(STAGE_ENTERED, { stage, feature }); this.eventBus.emit(STAGE_ENTERED, {stage, feature});
}); });
} }
@ -178,7 +180,7 @@ export class FeatureLifecycleService {
private async handleBulkMetrics(events: IClientMetricsEnv[]) { private async handleBulkMetrics(events: IClientMetricsEnv[]) {
try { try {
const { environments, allFeatures } = const {environments, allFeatures} =
this.extractUniqueEnvironmentsAndFeatures(events); this.extractUniqueEnvironmentsAndFeatures(events);
const envMap = await this.buildEnvironmentMap(); const envMap = await this.buildEnvironmentMap();
const featureEnvMap = const featureEnvMap =
@ -206,10 +208,10 @@ export class FeatureLifecycleService {
private extractUniqueEnvironmentsAndFeatures(events: IClientMetricsEnv[]) { private extractUniqueEnvironmentsAndFeatures(events: IClientMetricsEnv[]) {
const environments = [...new Set(events.map((e) => e.environment))]; const environments = [...new Set(events.map((e) => e.environment))];
const allFeatures = [...new Set(events.map((e) => e.featureName))]; const allFeatures = [...new Set(events.map((e) => e.featureName))];
return { environments, allFeatures }; return {environments, allFeatures};
} }
private async buildEnvironmentMap() { private async buildEnvironmentMap(): Promise<Map<string, IEnvironment>> {
const allEnvs = await this.environmentStore.getAll(); const allEnvs = await this.environmentStore.getAll();
return new Map(allEnvs.map((env) => [env.name, env])); return new Map(allEnvs.map((env) => [env.name, env]));
} }
@ -217,7 +219,10 @@ export class FeatureLifecycleService {
private async buildFeatureEnvironmentMap(allFeatures: string[]) { private async buildFeatureEnvironmentMap(allFeatures: string[]) {
const allFeatureEnvs = const allFeatureEnvs =
await this.featureEnvironmentStore.getAllByFeatures(allFeatures); await this.featureEnvironmentStore.getAllByFeatures(allFeatures);
const featureEnvMap = new Map<string, Map<string, any>>(); const featureEnvMap = new Map<
string,
Map<string, IFeatureEnvironment>
>();
allFeatureEnvs.forEach((fe) => { allFeatureEnvs.forEach((fe) => {
if (!featureEnvMap.has(fe.environment)) { if (!featureEnvMap.has(fe.environment)) {
@ -232,8 +237,8 @@ export class FeatureLifecycleService {
private determineLifecycleStages( private determineLifecycleStages(
events: IClientMetricsEnv[], events: IClientMetricsEnv[],
environments: string[], environments: string[],
envMap: Map<string, any>, envMap: Map<string, IEnvironment>,
featureEnvMap: Map<string, Map<string, any>>, featureEnvMap: Map<string, Map<string, IFeatureEnvironment>>,
): Array<{ feature: string; stage: 'pre-live' | 'live' }> { ): Array<{ feature: string; stage: 'pre-live' | 'live' }> {
const allStagesToInsert: Array<{ const allStagesToInsert: Array<{
feature: string; feature: string;
@ -286,13 +291,13 @@ export class FeatureLifecycleService {
private createLiveStages( private createLiveStages(
features: string[], features: string[],
): Array<{ feature: string; stage: 'live' }> { ): Array<{ feature: string; stage: 'live' }> {
return features.map((feature) => ({ feature, stage: 'live' as const })); return features.map((feature) => ({feature, stage: 'live' as const}));
} }
private getEnabledFeaturesForEnvironment( private getEnabledFeaturesForEnvironment(
features: string[], features: string[],
environment: string, environment: string,
featureEnvMap: Map<string, Map<string, any>>, featureEnvMap: Map<string, Map<string, IFeatureEnvironment>>,
): string[] { ): string[] {
const envFeatureEnvs = featureEnvMap.get(environment) || new Map(); const envFeatureEnvs = featureEnvMap.get(environment) || new Map();
return features.filter((feature) => { return features.filter((feature) => {
@ -320,7 +325,7 @@ export class FeatureLifecycleService {
new FeatureCompletedEvent({ new FeatureCompletedEvent({
project: projectId, project: projectId,
featureName: feature, featureName: feature,
data: { ...status, kept: status.status === 'kept' }, data: {...status, kept: status.status === 'kept'},
auditUser, auditUser,
}), }),
); );
@ -346,7 +351,7 @@ export class FeatureLifecycleService {
private async featureArchived(feature: string) { private async featureArchived(feature: string) {
const result = await this.featureLifecycleStore.insert([ const result = await this.featureLifecycleStore.insert([
{ feature, stage: 'archived' }, {feature, stage: 'archived'},
]); ]);
this.recordStagesEntered(result); this.recordStagesEntered(result);
} }