1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: feature lifecycle complete and uncomplete (#6927)

Creating a way to complete and uncomplete feature.
This commit is contained in:
Jaanus Sellin 2024-04-26 11:38:42 +03:00 committed by GitHub
parent 31ab38e162
commit 78b9299ff1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 300 additions and 26 deletions

View File

@ -7,6 +7,10 @@ import EventStore from '../../db/event-store';
import type { Db } from '../../db/db'; import type { Db } from '../../db/db';
import { FeatureLifecycleStore } from './feature-lifecycle-store'; import { FeatureLifecycleStore } from './feature-lifecycle-store';
import EnvironmentStore from '../project-environments/environment-store'; import EnvironmentStore from '../project-environments/environment-store';
import EventService from '../events/event-service';
import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store';
import { EventEmitter } from 'stream';
import FeatureTagStore from '../../db/feature-tag-store';
export const createFeatureLifecycleService = ( export const createFeatureLifecycleService = (
db: Db, db: Db,
@ -16,12 +20,24 @@ export const createFeatureLifecycleService = (
const eventStore = new EventStore(db, getLogger); const eventStore = new EventStore(db, getLogger);
const featureLifecycleStore = new FeatureLifecycleStore(db); const featureLifecycleStore = new FeatureLifecycleStore(db);
const environmentStore = new EnvironmentStore(db, eventBus, getLogger); const environmentStore = new EnvironmentStore(db, eventBus, getLogger);
const featureTagStore = new FeatureTagStore(
db,
config.eventBus,
config.getLogger,
);
const eventService = new EventService(
{ eventStore, featureTagStore },
{ getLogger, eventBus: new EventEmitter() },
);
const featureLifecycleService = new FeatureLifecycleService( const featureLifecycleService = new FeatureLifecycleService(
{ {
eventStore, eventStore,
featureLifecycleStore, featureLifecycleStore,
environmentStore, environmentStore,
}, },
{
eventService,
},
config, config,
); );
@ -37,12 +53,19 @@ export const createFakeFeatureLifecycleService = (config: IUnleashConfig) => {
const eventStore = new FakeEventStore(); const eventStore = new FakeEventStore();
const featureLifecycleStore = new FakeFeatureLifecycleStore(); const featureLifecycleStore = new FakeFeatureLifecycleStore();
const environmentStore = new FakeEnvironmentStore(); const environmentStore = new FakeEnvironmentStore();
const eventService = new EventService(
{ eventStore, featureTagStore: new FakeFeatureTagStore() },
config,
);
const featureLifecycleService = new FeatureLifecycleService( const featureLifecycleService = new FeatureLifecycleService(
{ {
eventStore, eventStore,
featureLifecycleStore, featureLifecycleStore,
environmentStore, environmentStore,
}, },
{
eventService,
},
config, config,
); );

View File

@ -43,4 +43,14 @@ export class FakeFeatureLifecycleStore implements IFeatureLifecycleStore {
const lifecycle = await this.get(stage.feature); const lifecycle = await this.get(stage.feature);
return Boolean(lifecycle.find((s) => s.stage === stage.stage)); return Boolean(lifecycle.find((s) => s.stage === stage.stage));
} }
async deleteStage(stage: FeatureLifecycleStage): Promise<void> {
if (!this.lifecycles[stage.feature]) {
return;
}
const updatedStages = this.lifecycles[stage.feature].filter(
(s) => s.stage !== stage.stage,
);
this.lifecycles[stage.feature] = updatedStages;
}
} }

View File

@ -5,10 +5,12 @@ import {
type IUnleashServices, type IUnleashServices,
NONE, NONE,
serializeDates, serializeDates,
UPDATE_FEATURE,
} from '../../types'; } from '../../types';
import type { OpenApiService } from '../../services'; import type { OpenApiService } from '../../services';
import { import {
createResponseSchema, createResponseSchema,
emptyResponse,
featureLifecycleSchema, featureLifecycleSchema,
type FeatureLifecycleSchema, type FeatureLifecycleSchema,
getStandardResponses, getStandardResponses,
@ -16,6 +18,7 @@ import {
import Controller from '../../routes/controller'; import Controller from '../../routes/controller';
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import { NotFoundError } from '../../error'; import { NotFoundError } from '../../error';
import type { IAuthRequest } from '../../routes/unleash-types';
interface FeatureLifecycleParams { interface FeatureLifecycleParams {
projectId: string; projectId: string;
@ -62,6 +65,46 @@ export default class FeatureLifecycleController extends Controller {
}), }),
], ],
}); });
this.route({
method: 'post',
path: `${PATH}/complete`,
handler: this.complete,
permission: UPDATE_FEATURE,
acceptAnyContentType: true,
middleware: [
openApiService.validPath({
tags: ['Unstable'],
summary: 'Set feature completed',
description: 'This will set the feature as completed.',
operationId: 'complete',
responses: {
200: emptyResponse,
...getStandardResponses(401, 403, 404),
},
}),
],
});
this.route({
method: 'post',
path: `${PATH}/uncomplete`,
handler: this.uncomplete,
permission: UPDATE_FEATURE,
acceptAnyContentType: true,
middleware: [
openApiService.validPath({
tags: ['Unstable'],
summary: 'Set feature uncompleted',
description: 'This will set the feature as uncompleted.',
operationId: 'uncomplete',
responses: {
200: emptyResponse,
...getStandardResponses(401, 403, 404),
},
}),
],
});
} }
async getFeatureLifecycle( async getFeatureLifecycle(
@ -83,4 +126,38 @@ export default class FeatureLifecycleController extends Controller {
serializeDates(result), serializeDates(result),
); );
} }
async complete(
req: IAuthRequest<FeatureLifecycleParams>,
res: Response,
): Promise<void> {
if (!this.flagResolver.isEnabled('featureLifecycle')) {
throw new NotFoundError('Feature lifecycle is disabled.');
}
const { featureName } = req.params;
await this.featureLifecycleService.featureCompleted(
featureName,
req.audit,
);
res.status(200).end();
}
async uncomplete(
req: IAuthRequest<FeatureLifecycleParams>,
res: Response,
): Promise<void> {
if (!this.flagResolver.isEnabled('featureLifecycle')) {
throw new NotFoundError('Feature lifecycle is disabled.');
}
const { featureName } = req.params;
await this.featureLifecycleService.featureUnCompleted(
featureName,
req.audit,
);
res.status(200).end();
}
} }

View File

@ -68,8 +68,6 @@ test('can insert and read lifecycle stages', async () => {
emitMetricsEvent('my-prod-environment'); emitMetricsEvent('my-prod-environment');
emitMetricsEvent('my-another-prod-environment'); emitMetricsEvent('my-another-prod-environment');
eventStore.emit(FEATURE_COMPLETED, { featureName });
await reachedStage('completed');
eventStore.emit(FEATURE_ARCHIVED, { featureName }); eventStore.emit(FEATURE_ARCHIVED, { featureName });
await reachedStage('archived'); await reachedStage('archived');
@ -80,7 +78,6 @@ test('can insert and read lifecycle stages', async () => {
{ stage: 'initial', enteredStageAt: expect.any(Date) }, { stage: 'initial', enteredStageAt: expect.any(Date) },
{ stage: 'pre-live', enteredStageAt: expect.any(Date) }, { stage: 'pre-live', enteredStageAt: expect.any(Date) },
{ stage: 'live', enteredStageAt: expect.any(Date) }, { stage: 'live', enteredStageAt: expect.any(Date) },
{ stage: 'completed', enteredStageAt: expect.any(Date) },
{ stage: 'archived', enteredStageAt: expect.any(Date) }, { stage: 'archived', enteredStageAt: expect.any(Date) },
]); ]);

View File

@ -1,9 +1,11 @@
import { import {
CLIENT_METRICS, CLIENT_METRICS,
FEATURE_ARCHIVED, FEATURE_ARCHIVED,
FEATURE_COMPLETED,
FEATURE_CREATED, FEATURE_CREATED,
FEATURE_REVIVED, FEATURE_REVIVED,
FeatureCompletedEvent,
FeatureUncompletedEvent,
type IAuditUser,
type IEnvironmentStore, type IEnvironmentStore,
type IEventStore, type IEventStore,
type IFlagResolver, type IFlagResolver,
@ -15,6 +17,7 @@ import type {
} from './feature-lifecycle-store-type'; } from './feature-lifecycle-store-type';
import EventEmitter from 'events'; import EventEmitter from 'events';
import type { Logger } from '../../logger'; import type { Logger } from '../../logger';
import type EventService from '../events/event-service';
import type { ValidatedClientMetrics } from '../metrics/shared/schema'; import type { ValidatedClientMetrics } from '../metrics/shared/schema';
export const STAGE_ENTERED = 'STAGE_ENTERED'; export const STAGE_ENTERED = 'STAGE_ENTERED';
@ -30,6 +33,8 @@ export class FeatureLifecycleService extends EventEmitter {
private eventBus: EventEmitter; private eventBus: EventEmitter;
private eventService: EventService;
private logger: Logger; private logger: Logger;
constructor( constructor(
@ -42,6 +47,11 @@ export class FeatureLifecycleService extends EventEmitter {
environmentStore: IEnvironmentStore; environmentStore: IEnvironmentStore;
featureLifecycleStore: IFeatureLifecycleStore; featureLifecycleStore: IFeatureLifecycleStore;
}, },
{
eventService,
}: {
eventService: EventService;
},
{ {
flagResolver, flagResolver,
eventBus, eventBus,
@ -54,6 +64,7 @@ export class FeatureLifecycleService extends EventEmitter {
this.environmentStore = environmentStore; this.environmentStore = environmentStore;
this.flagResolver = flagResolver; this.flagResolver = flagResolver;
this.eventBus = eventBus; this.eventBus = eventBus;
this.eventService = eventService;
this.logger = getLogger( this.logger = getLogger(
'feature-lifecycle/feature-lifecycle-service.ts', 'feature-lifecycle/feature-lifecycle-service.ts',
); );
@ -84,11 +95,6 @@ export class FeatureLifecycleService extends EventEmitter {
} }
}, },
); );
this.eventStore.on(FEATURE_COMPLETED, async (event) => {
await this.checkEnabled(() =>
this.featureCompleted(event.featureName),
);
});
this.eventStore.on(FEATURE_ARCHIVED, async (event) => { this.eventStore.on(FEATURE_ARCHIVED, async (event) => {
await this.checkEnabled(() => await this.checkEnabled(() =>
this.featureArchived(event.featureName), this.featureArchived(event.featureName),
@ -145,14 +151,32 @@ export class FeatureLifecycleService extends EventEmitter {
} }
} }
private async featureCompleted(feature: string) { public async featureCompleted(feature: string, auditUser: IAuditUser) {
await this.featureLifecycleStore.insert([ await this.featureLifecycleStore.insert([
{ {
feature, feature,
stage: 'completed', stage: 'completed',
}, },
]); ]);
this.emit(STAGE_ENTERED, { stage: 'completed' }); await this.eventService.storeEvent(
new FeatureCompletedEvent({
featureName: feature,
auditUser,
}),
);
}
public async featureUnCompleted(feature: string, auditUser: IAuditUser) {
await this.featureLifecycleStore.deleteStage({
feature,
stage: 'completed',
});
await this.eventService.storeEvent(
new FeatureUncompletedEvent({
featureName: feature,
auditUser,
}),
);
} }
private async featureArchived(feature: string) { private async featureArchived(feature: string) {

View File

@ -12,4 +12,5 @@ export interface IFeatureLifecycleStore {
get(feature: string): Promise<FeatureLifecycleView>; get(feature: string): Promise<FeatureLifecycleView>;
stageExists(stage: FeatureLifecycleStage): Promise<boolean>; stageExists(stage: FeatureLifecycleStage): Promise<boolean>;
delete(feature: string): Promise<void>; delete(feature: string): Promise<void>;
deleteStage(stage: FeatureLifecycleStage): Promise<void>;
} }

View File

@ -62,6 +62,15 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore {
await this.db('feature_lifecycles').where({ feature }).del(); await this.db('feature_lifecycles').where({ feature }).del();
} }
async deleteStage(stage: FeatureLifecycleStage): Promise<void> {
await this.db('feature_lifecycles')
.where({
stage: stage.stage,
feature: stage.feature,
})
.del();
}
async stageExists(stage: FeatureLifecycleStage): Promise<boolean> { async stageExists(stage: FeatureLifecycleStage): Promise<boolean> {
const result = await this.db.raw( const result = await this.db.raw(
`SELECT EXISTS(SELECT 1 FROM feature_lifecycles WHERE stage = ? and feature = ?) AS present`, `SELECT EXISTS(SELECT 1 FROM feature_lifecycles WHERE stage = ? and feature = ?) AS present`,

View File

@ -61,6 +61,21 @@ const getFeatureLifecycle = async (featureName: string, expectedCode = 200) => {
.get(`/api/admin/projects/default/features/${featureName}/lifecycle`) .get(`/api/admin/projects/default/features/${featureName}/lifecycle`)
.expect(expectedCode); .expect(expectedCode);
}; };
const completeFeature = async (featureName: string, expectedCode = 200) => {
return app.request
.post(
`/api/admin/projects/default/features/${featureName}/lifecycle/complete`,
)
.expect(expectedCode);
};
const uncompleteFeature = async (featureName: string, expectedCode = 200) => {
return app.request
.post(
`/api/admin/projects/default/features/${featureName}/lifecycle/uncomplete`,
)
.expect(expectedCode);
};
function reachedStage(name: StageName) { function reachedStage(name: StageName) {
return new Promise((resolve) => return new Promise((resolve) =>
@ -70,10 +85,10 @@ function reachedStage(name: StageName) {
); );
} }
const expectFeatureStage = async (stage: StageName) => { const expectFeatureStage = async (featureName: string, stage: StageName) => {
const { body: feature } = await app.getProjectFeatures( const { body: feature } = await app.getProjectFeatures(
'default', 'default',
'my_feature_a', featureName,
); );
expect(feature.lifecycle).toMatchObject({ expect(feature.lifecycle).toMatchObject({
stage, stage,
@ -85,7 +100,7 @@ test('should return lifecycle stages', async () => {
await app.createFeature('my_feature_a'); await app.createFeature('my_feature_a');
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' }); eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' });
await reachedStage('initial'); await reachedStage('initial');
await expectFeatureStage('initial'); await expectFeatureStage('my_feature_a', 'initial');
eventBus.emit(CLIENT_METRICS, { eventBus.emit(CLIENT_METRICS, {
bucket: { bucket: {
toggles: { toggles: {
@ -110,20 +125,42 @@ test('should return lifecycle stages', async () => {
environment: 'non-existent', environment: 'non-existent',
}); });
await reachedStage('live'); await reachedStage('live');
await expectFeatureStage('live'); await expectFeatureStage('my_feature_a', 'live');
eventStore.emit(FEATURE_ARCHIVED, { featureName: 'my_feature_a' }); eventStore.emit(FEATURE_ARCHIVED, { featureName: 'my_feature_a' });
await reachedStage('archived'); await reachedStage('archived');
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: 'live', enteredStageAt: expect.any(String) }, stage: 'initial',
{ stage: 'archived', enteredStageAt: expect.any(String) }, enteredStageAt: expect.any(String),
},
{
stage: 'live',
enteredStageAt: expect.any(String),
},
{
stage: 'archived',
enteredStageAt: expect.any(String),
},
]); ]);
await expectFeatureStage('my_feature_a', 'archived');
await expectFeatureStage('archived');
eventStore.emit(FEATURE_REVIVED, { featureName: 'my_feature_a' }); eventStore.emit(FEATURE_REVIVED, { featureName: 'my_feature_a' });
await reachedStage('initial'); await reachedStage('initial');
}); });
test('should be able to toggle between completed/uncompleted', async () => {
await app.createFeature('my_feature_b');
await completeFeature('my_feature_b');
await expectFeatureStage('my_feature_b', 'completed');
await uncompleteFeature('my_feature_b');
const { body } = await getFeatureLifecycle('my_feature_b');
expect(body).toEqual([]);
});

View File

@ -29,6 +29,7 @@ export const DROP_FEATURE_TAGS = 'drop-feature-tags' as const;
export const FEATURE_UNTAGGED = 'feature-untagged' as const; export const FEATURE_UNTAGGED = 'feature-untagged' as const;
export const FEATURE_STALE_ON = 'feature-stale-on' as const; export const FEATURE_STALE_ON = 'feature-stale-on' as const;
export const FEATURE_COMPLETED = 'feature-completed' as const; export const FEATURE_COMPLETED = 'feature-completed' as const;
export const FEATURE_UNCOMPLETED = 'feature-uncompleted' as const;
export const FEATURE_STALE_OFF = 'feature-stale-off' as const; export const FEATURE_STALE_OFF = 'feature-stale-off' as const;
export const DROP_FEATURES = 'drop-features' as const; export const DROP_FEATURES = 'drop-features' as const;
export const FEATURE_ENVIRONMENT_ENABLED = export const FEATURE_ENVIRONMENT_ENABLED =
@ -208,6 +209,8 @@ export const IEventTypes = [
FEATURE_STRATEGY_ADD, FEATURE_STRATEGY_ADD,
FEATURE_STRATEGY_REMOVE, FEATURE_STRATEGY_REMOVE,
FEATURE_TYPE_UPDATED, FEATURE_TYPE_UPDATED,
FEATURE_COMPLETED,
FEATURE_UNCOMPLETED,
STRATEGY_ORDER_CHANGED, STRATEGY_ORDER_CHANGED,
DROP_FEATURE_TAGS, DROP_FEATURE_TAGS,
FEATURE_UNTAGGED, FEATURE_UNTAGGED,
@ -370,6 +373,7 @@ class BaseEvent implements IBaseEvent {
readonly createdByUserId: number; readonly createdByUserId: number;
readonly ip: string; readonly ip: string;
/** /**
* @param type the type of the event we're creating. * @param type the type of the event we're creating.
* @param auditUser User info used to track which user performed the action. Includes username (email or username), userId and ip * @param auditUser User info used to track which user performed the action. Includes username (email or username), userId and ip
@ -424,7 +428,11 @@ export class FeatureEnvironmentEvent extends BaseEvent {
this.environment = p.environment; this.environment = p.environment;
} }
} }
export type StrategyIds = { strategyIds: string[] };
export type StrategyIds = {
strategyIds: string[];
};
export class StrategiesOrderChangedEvent extends BaseEvent { export class StrategiesOrderChangedEvent extends BaseEvent {
readonly project: string; readonly project: string;
@ -459,9 +467,13 @@ export class FeatureVariantEvent extends BaseEvent {
readonly featureName: string; readonly featureName: string;
readonly data: { variants: IVariant[] }; readonly data: {
variants: IVariant[];
};
readonly preData: { variants: IVariant[] }; readonly preData: {
variants: IVariant[];
};
constructor(p: { constructor(p: {
project: string; project: string;
@ -485,9 +497,13 @@ export class EnvironmentVariantEvent extends BaseEvent {
readonly featureName: string; readonly featureName: string;
readonly data: { variants: IVariant[] }; readonly data: {
variants: IVariant[];
};
readonly preData: { variants: IVariant[] }; readonly preData: {
variants: IVariant[];
};
/** /**
*/ */
@ -507,9 +523,11 @@ export class EnvironmentVariantEvent extends BaseEvent {
this.preData = { variants: p.oldVariants }; this.preData = { variants: p.oldVariants };
} }
} }
export class ProjectCreatedEvent extends BaseEvent { export class ProjectCreatedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
project: string; project: string;
@ -525,6 +543,7 @@ export class ProjectUpdatedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly data: any; readonly data: any;
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
preData: any; preData: any;
@ -540,6 +559,7 @@ export class ProjectUpdatedEvent extends BaseEvent {
export class ProjectDeletedEvent extends BaseEvent { export class ProjectDeletedEvent extends BaseEvent {
readonly project: string; readonly project: string;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -552,6 +572,7 @@ export class ProjectDeletedEvent extends BaseEvent {
export class RoleUpdatedEvent extends BaseEvent { export class RoleUpdatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -562,6 +583,7 @@ export class RoleUpdatedEvent extends BaseEvent {
this.preData = eventData.preData; this.preData = eventData.preData;
} }
} }
export class FeatureChangeProjectEvent extends BaseEvent { export class FeatureChangeProjectEvent extends BaseEvent {
readonly project: string; readonly project: string;
@ -582,7 +604,10 @@ export class FeatureChangeProjectEvent extends BaseEvent {
const { newProject, oldProject, featureName } = p; const { newProject, oldProject, featureName } = p;
this.project = newProject; this.project = newProject;
this.featureName = featureName; this.featureName = featureName;
this.data = { newProject, oldProject }; this.data = {
newProject,
oldProject,
};
} }
} }
@ -607,10 +632,37 @@ export class FeatureCreatedEvent extends BaseEvent {
} }
} }
export class FeatureCompletedEvent extends BaseEvent {
readonly featureName: string;
constructor(p: {
featureName: string;
auditUser: IAuditUser;
}) {
super(FEATURE_COMPLETED, p.auditUser);
const { featureName } = p;
this.featureName = featureName;
}
}
export class FeatureUncompletedEvent extends BaseEvent {
readonly featureName: string;
constructor(p: {
featureName: string;
auditUser: IAuditUser;
}) {
super(FEATURE_UNCOMPLETED, p.auditUser);
const { featureName } = p;
this.featureName = featureName;
}
}
export class FeatureUpdatedEvent extends BaseEvent { export class FeatureUpdatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
readonly featureName: string; readonly featureName: string;
readonly project: string; readonly project: string;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
featureName: string; featureName: string;
@ -628,6 +680,7 @@ export class FeatureTaggedEvent extends BaseEvent {
readonly data: any; readonly data: any;
readonly featureName: string; readonly featureName: string;
readonly project: string; readonly project: string;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
featureName: string; featureName: string;
@ -644,6 +697,7 @@ export class FeatureTaggedEvent extends BaseEvent {
export class FeatureTypeUpdatedEvent extends BaseEvent { export class FeatureTypeUpdatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
preData: any; preData: any;
@ -654,10 +708,12 @@ export class FeatureTypeUpdatedEvent extends BaseEvent {
this.preData = eventData.preData; this.preData = eventData.preData;
} }
} }
export class FeatureDependencyAddedEvent extends BaseEvent { export class FeatureDependencyAddedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly featureName: string; readonly featureName: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
featureName: string; featureName: string;
@ -675,6 +731,7 @@ export class FeatureDependencyRemovedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly featureName: string; readonly featureName: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
featureName: string; featureName: string;
@ -687,6 +744,7 @@ export class FeatureDependencyRemovedEvent extends BaseEvent {
this.data = eventData.data; this.data = eventData.data;
} }
} }
export class FeatureDependenciesRemovedEvent extends BaseEvent { export class FeatureDependenciesRemovedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly featureName: string; readonly featureName: string;
@ -705,6 +763,7 @@ export class FeatureDependenciesRemovedEvent extends BaseEvent {
export class FeaturesImportedEvent extends BaseEvent { export class FeaturesImportedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly environment: string; readonly environment: string;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
environment: string; environment: string;
@ -892,6 +951,7 @@ export class FeatureStrategyRemoveEvent extends BaseEvent {
export class FeatureFavoritedEvent extends BaseEvent { export class FeatureFavoritedEvent extends BaseEvent {
readonly featureName: string; readonly featureName: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
featureName: string; featureName: string;
data: any; data: any;
@ -906,6 +966,7 @@ export class FeatureFavoritedEvent extends BaseEvent {
export class ProjectFavoritedEvent extends BaseEvent { export class ProjectFavoritedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
data: any; data: any;
@ -920,6 +981,7 @@ export class ProjectFavoritedEvent extends BaseEvent {
export class FeatureUnfavoritedEvent extends BaseEvent { export class FeatureUnfavoritedEvent extends BaseEvent {
readonly featureName: string; readonly featureName: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
featureName: string; featureName: string;
data: any; data: any;
@ -934,6 +996,7 @@ export class FeatureUnfavoritedEvent extends BaseEvent {
export class ProjectUnfavoritedEvent extends BaseEvent { export class ProjectUnfavoritedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
data: any; data: any;
@ -1367,6 +1430,7 @@ export class UserDeletedEvent extends BaseEvent {
export class TagTypeCreatedEvent extends BaseEvent { export class TagTypeCreatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1378,6 +1442,7 @@ export class TagTypeCreatedEvent extends BaseEvent {
export class TagTypeDeletedEvent extends BaseEvent { export class TagTypeDeletedEvent extends BaseEvent {
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
preData: any; preData: any;
@ -1389,6 +1454,7 @@ export class TagTypeDeletedEvent extends BaseEvent {
export class TagTypeUpdatedEvent extends BaseEvent { export class TagTypeUpdatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1400,6 +1466,7 @@ export class TagTypeUpdatedEvent extends BaseEvent {
export class TagCreatedEvent extends BaseEvent { export class TagCreatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1411,6 +1478,7 @@ export class TagCreatedEvent extends BaseEvent {
export class TagDeletedEvent extends BaseEvent { export class TagDeletedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1422,6 +1490,7 @@ export class TagDeletedEvent extends BaseEvent {
export class PatCreatedEvent extends BaseEvent { export class PatCreatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1430,8 +1499,10 @@ export class PatCreatedEvent extends BaseEvent {
this.data = eventData.data; this.data = eventData.data;
} }
} }
export class PatDeletedEvent extends BaseEvent { export class PatDeletedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1444,6 +1515,7 @@ export class PatDeletedEvent extends BaseEvent {
export class ProjectEnvironmentAdded extends BaseEvent { export class ProjectEnvironmentAdded extends BaseEvent {
readonly project: string; readonly project: string;
readonly environment: string; readonly environment: string;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
environment: string; environment: string;
@ -1458,6 +1530,7 @@ export class ProjectEnvironmentAdded extends BaseEvent {
export class ProjectEnvironmentRemoved extends BaseEvent { export class ProjectEnvironmentRemoved extends BaseEvent {
readonly project: string; readonly project: string;
readonly environment: string; readonly environment: string;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
environment: string; environment: string;
@ -1471,6 +1544,7 @@ export class ProjectEnvironmentRemoved extends BaseEvent {
export class FeaturesExportedEvent extends BaseEvent { export class FeaturesExportedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1482,6 +1556,7 @@ export class FeaturesExportedEvent extends BaseEvent {
export class RoleCreatedEvent extends BaseEvent { export class RoleCreatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -1493,6 +1568,7 @@ export class RoleCreatedEvent extends BaseEvent {
export class RoleDeletedEvent extends BaseEvent { export class RoleDeletedEvent extends BaseEvent {
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
preData: any; preData: any;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -1501,8 +1577,10 @@ export class RoleDeletedEvent extends BaseEvent {
this.preData = eventData.preData; this.preData = eventData.preData;
} }
} }
export class StrategyCreatedEvent extends BaseEvent { export class StrategyCreatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -1511,8 +1589,10 @@ export class StrategyCreatedEvent extends BaseEvent {
this.data = eventData.data; this.data = eventData.data;
} }
} }
export class StrategyUpdatedEvent extends BaseEvent { export class StrategyUpdatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -1521,8 +1601,10 @@ export class StrategyUpdatedEvent extends BaseEvent {
this.data = eventData.data; this.data = eventData.data;
} }
} }
export class StrategyDeletedEvent extends BaseEvent { export class StrategyDeletedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -1531,8 +1613,10 @@ export class StrategyDeletedEvent extends BaseEvent {
this.data = eventData.data; this.data = eventData.data;
} }
} }
export class StrategyDeprecatedEvent extends BaseEvent { export class StrategyDeprecatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -1541,8 +1625,10 @@ export class StrategyDeprecatedEvent extends BaseEvent {
this.data = eventData.data; this.data = eventData.data;
} }
} }
export class StrategyReactivatedEvent extends BaseEvent { export class StrategyReactivatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
auditUser: IAuditUser; auditUser: IAuditUser;
@ -1557,6 +1643,7 @@ export class DefaultStrategyUpdatedEvent extends BaseEvent {
readonly environment: string; readonly environment: string;
readonly preData: any; readonly preData: any;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
project: string; project: string;
environment: string; environment: string;
@ -1574,6 +1661,7 @@ export class DefaultStrategyUpdatedEvent extends BaseEvent {
export class AddonConfigCreatedEvent extends BaseEvent { export class AddonConfigCreatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1586,6 +1674,7 @@ export class AddonConfigCreatedEvent extends BaseEvent {
export class AddonConfigUpdatedEvent extends BaseEvent { export class AddonConfigUpdatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
data: any; data: any;
@ -1599,6 +1688,7 @@ export class AddonConfigUpdatedEvent extends BaseEvent {
export class AddonConfigDeletedEvent extends BaseEvent { export class AddonConfigDeletedEvent extends BaseEvent {
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
preData: any; preData: any;
@ -1611,6 +1701,7 @@ export class AddonConfigDeletedEvent extends BaseEvent {
export class SegmentCreatedEvent extends BaseEvent { export class SegmentCreatedEvent extends BaseEvent {
readonly project: string; readonly project: string;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
project: string; project: string;
@ -1626,6 +1717,7 @@ export class SegmentUpdatedEvent extends BaseEvent {
readonly data: any; readonly data: any;
readonly preData: any; readonly preData: any;
readonly project: string; readonly project: string;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
project: string; project: string;
@ -1638,9 +1730,11 @@ export class SegmentUpdatedEvent extends BaseEvent {
this.preData = eventData.preData; this.preData = eventData.preData;
} }
} }
export class SegmentDeletedEvent extends BaseEvent { export class SegmentDeletedEvent extends BaseEvent {
readonly preData: any; readonly preData: any;
readonly project?: string; readonly project?: string;
constructor(eventData: { constructor(eventData: {
auditUser: IAuditUser; auditUser: IAuditUser;
preData: any; preData: any;
@ -1655,6 +1749,7 @@ export class SegmentDeletedEvent extends BaseEvent {
export class GroupUpdatedEvent extends BaseEvent { export class GroupUpdatedEvent extends BaseEvent {
readonly preData: any; readonly preData: any;
readonly data: any; readonly data: any;
constructor(eventData: { constructor(eventData: {
data: any; data: any;
preData: any; preData: any;
@ -1668,6 +1763,7 @@ export class GroupUpdatedEvent extends BaseEvent {
export class GroupDeletedEvent extends BaseEvent { export class GroupDeletedEvent extends BaseEvent {
readonly preData: any; readonly preData: any;
constructor(eventData: { constructor(eventData: {
preData: any; preData: any;
auditUser: IAuditUser; auditUser: IAuditUser;