mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: feature lifecycle complete and uncomplete (#6927)
Creating a way to complete and uncomplete feature.
This commit is contained in:
		
							parent
							
								
									31ab38e162
								
							
						
					
					
						commit
						78b9299ff1
					
				| @ -7,6 +7,10 @@ import EventStore from '../../db/event-store'; | ||||
| import type { Db } from '../../db/db'; | ||||
| import { FeatureLifecycleStore } from './feature-lifecycle-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 = ( | ||||
|     db: Db, | ||||
| @ -16,12 +20,24 @@ export const createFeatureLifecycleService = ( | ||||
|     const eventStore = new EventStore(db, getLogger); | ||||
|     const featureLifecycleStore = new FeatureLifecycleStore(db); | ||||
|     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( | ||||
|         { | ||||
|             eventStore, | ||||
|             featureLifecycleStore, | ||||
|             environmentStore, | ||||
|         }, | ||||
|         { | ||||
|             eventService, | ||||
|         }, | ||||
|         config, | ||||
|     ); | ||||
| 
 | ||||
| @ -37,12 +53,19 @@ export const createFakeFeatureLifecycleService = (config: IUnleashConfig) => { | ||||
|     const eventStore = new FakeEventStore(); | ||||
|     const featureLifecycleStore = new FakeFeatureLifecycleStore(); | ||||
|     const environmentStore = new FakeEnvironmentStore(); | ||||
|     const eventService = new EventService( | ||||
|         { eventStore, featureTagStore: new FakeFeatureTagStore() }, | ||||
|         config, | ||||
|     ); | ||||
|     const featureLifecycleService = new FeatureLifecycleService( | ||||
|         { | ||||
|             eventStore, | ||||
|             featureLifecycleStore, | ||||
|             environmentStore, | ||||
|         }, | ||||
|         { | ||||
|             eventService, | ||||
|         }, | ||||
|         config, | ||||
|     ); | ||||
| 
 | ||||
|  | ||||
| @ -43,4 +43,14 @@ export class FakeFeatureLifecycleStore implements IFeatureLifecycleStore { | ||||
|         const lifecycle = await this.get(stage.feature); | ||||
|         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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -5,10 +5,12 @@ import { | ||||
|     type IUnleashServices, | ||||
|     NONE, | ||||
|     serializeDates, | ||||
|     UPDATE_FEATURE, | ||||
| } from '../../types'; | ||||
| import type { OpenApiService } from '../../services'; | ||||
| import { | ||||
|     createResponseSchema, | ||||
|     emptyResponse, | ||||
|     featureLifecycleSchema, | ||||
|     type FeatureLifecycleSchema, | ||||
|     getStandardResponses, | ||||
| @ -16,6 +18,7 @@ import { | ||||
| import Controller from '../../routes/controller'; | ||||
| import type { Request, Response } from 'express'; | ||||
| import { NotFoundError } from '../../error'; | ||||
| import type { IAuthRequest } from '../../routes/unleash-types'; | ||||
| 
 | ||||
| interface FeatureLifecycleParams { | ||||
|     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( | ||||
| @ -83,4 +126,38 @@ export default class FeatureLifecycleController extends Controller { | ||||
|             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(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -68,8 +68,6 @@ test('can insert and read lifecycle stages', async () => { | ||||
|     emitMetricsEvent('my-prod-environment'); | ||||
|     emitMetricsEvent('my-another-prod-environment'); | ||||
| 
 | ||||
|     eventStore.emit(FEATURE_COMPLETED, { featureName }); | ||||
|     await reachedStage('completed'); | ||||
|     eventStore.emit(FEATURE_ARCHIVED, { featureName }); | ||||
|     await reachedStage('archived'); | ||||
| 
 | ||||
| @ -80,7 +78,6 @@ test('can insert and read lifecycle stages', async () => { | ||||
|         { stage: 'initial', enteredStageAt: expect.any(Date) }, | ||||
|         { stage: 'pre-live', enteredStageAt: expect.any(Date) }, | ||||
|         { stage: 'live', enteredStageAt: expect.any(Date) }, | ||||
|         { stage: 'completed', enteredStageAt: expect.any(Date) }, | ||||
|         { stage: 'archived', enteredStageAt: expect.any(Date) }, | ||||
|     ]); | ||||
| 
 | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| import { | ||||
|     CLIENT_METRICS, | ||||
|     FEATURE_ARCHIVED, | ||||
|     FEATURE_COMPLETED, | ||||
|     FEATURE_CREATED, | ||||
|     FEATURE_REVIVED, | ||||
|     FeatureCompletedEvent, | ||||
|     FeatureUncompletedEvent, | ||||
|     type IAuditUser, | ||||
|     type IEnvironmentStore, | ||||
|     type IEventStore, | ||||
|     type IFlagResolver, | ||||
| @ -15,6 +17,7 @@ import type { | ||||
| } from './feature-lifecycle-store-type'; | ||||
| import EventEmitter from 'events'; | ||||
| import type { Logger } from '../../logger'; | ||||
| import type EventService from '../events/event-service'; | ||||
| import type { ValidatedClientMetrics } from '../metrics/shared/schema'; | ||||
| 
 | ||||
| export const STAGE_ENTERED = 'STAGE_ENTERED'; | ||||
| @ -30,6 +33,8 @@ export class FeatureLifecycleService extends EventEmitter { | ||||
| 
 | ||||
|     private eventBus: EventEmitter; | ||||
| 
 | ||||
|     private eventService: EventService; | ||||
| 
 | ||||
|     private logger: Logger; | ||||
| 
 | ||||
|     constructor( | ||||
| @ -42,6 +47,11 @@ export class FeatureLifecycleService extends EventEmitter { | ||||
|             environmentStore: IEnvironmentStore; | ||||
|             featureLifecycleStore: IFeatureLifecycleStore; | ||||
|         }, | ||||
|         { | ||||
|             eventService, | ||||
|         }: { | ||||
|             eventService: EventService; | ||||
|         }, | ||||
|         { | ||||
|             flagResolver, | ||||
|             eventBus, | ||||
| @ -54,6 +64,7 @@ export class FeatureLifecycleService extends EventEmitter { | ||||
|         this.environmentStore = environmentStore; | ||||
|         this.flagResolver = flagResolver; | ||||
|         this.eventBus = eventBus; | ||||
|         this.eventService = eventService; | ||||
|         this.logger = getLogger( | ||||
|             '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) => { | ||||
|             await this.checkEnabled(() => | ||||
|                 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([ | ||||
|             { | ||||
|                 feature, | ||||
|                 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) { | ||||
|  | ||||
| @ -12,4 +12,5 @@ export interface IFeatureLifecycleStore { | ||||
|     get(feature: string): Promise<FeatureLifecycleView>; | ||||
|     stageExists(stage: FeatureLifecycleStage): Promise<boolean>; | ||||
|     delete(feature: string): Promise<void>; | ||||
|     deleteStage(stage: FeatureLifecycleStage): Promise<void>; | ||||
| } | ||||
|  | ||||
| @ -62,6 +62,15 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore { | ||||
|         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> { | ||||
|         const result = await this.db.raw( | ||||
|             `SELECT EXISTS(SELECT 1 FROM feature_lifecycles WHERE stage = ? and feature = ?) AS present`, | ||||
|  | ||||
| @ -61,6 +61,21 @@ const getFeatureLifecycle = async (featureName: string, expectedCode = 200) => { | ||||
|         .get(`/api/admin/projects/default/features/${featureName}/lifecycle`) | ||||
|         .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) { | ||||
|     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( | ||||
|         'default', | ||||
|         'my_feature_a', | ||||
|         featureName, | ||||
|     ); | ||||
|     expect(feature.lifecycle).toMatchObject({ | ||||
|         stage, | ||||
| @ -85,7 +100,7 @@ test('should return lifecycle stages', async () => { | ||||
|     await app.createFeature('my_feature_a'); | ||||
|     eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' }); | ||||
|     await reachedStage('initial'); | ||||
|     await expectFeatureStage('initial'); | ||||
|     await expectFeatureStage('my_feature_a', 'initial'); | ||||
|     eventBus.emit(CLIENT_METRICS, { | ||||
|         bucket: { | ||||
|             toggles: { | ||||
| @ -110,20 +125,42 @@ test('should return lifecycle stages', async () => { | ||||
|         environment: 'non-existent', | ||||
|     }); | ||||
|     await reachedStage('live'); | ||||
|     await expectFeatureStage('live'); | ||||
|     await expectFeatureStage('my_feature_a', 'live'); | ||||
|     eventStore.emit(FEATURE_ARCHIVED, { featureName: 'my_feature_a' }); | ||||
|     await reachedStage('archived'); | ||||
| 
 | ||||
|     const { body } = await getFeatureLifecycle('my_feature_a'); | ||||
| 
 | ||||
|     expect(body).toEqual([ | ||||
|         { stage: 'initial', enteredStageAt: expect.any(String) }, | ||||
|         { stage: 'live', enteredStageAt: expect.any(String) }, | ||||
|         { stage: 'archived', enteredStageAt: expect.any(String) }, | ||||
|         { | ||||
|             stage: 'initial', | ||||
|             enteredStageAt: expect.any(String), | ||||
|         }, | ||||
|         { | ||||
|             stage: 'live', | ||||
|             enteredStageAt: expect.any(String), | ||||
|         }, | ||||
|         { | ||||
|             stage: 'archived', | ||||
|             enteredStageAt: expect.any(String), | ||||
|         }, | ||||
|     ]); | ||||
| 
 | ||||
|     await expectFeatureStage('archived'); | ||||
|     await expectFeatureStage('my_feature_a', 'archived'); | ||||
| 
 | ||||
|     eventStore.emit(FEATURE_REVIVED, { featureName: 'my_feature_a' }); | ||||
|     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([]); | ||||
| }); | ||||
|  | ||||
| @ -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_STALE_ON = 'feature-stale-on' 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 DROP_FEATURES = 'drop-features' as const; | ||||
| export const FEATURE_ENVIRONMENT_ENABLED = | ||||
| @ -208,6 +209,8 @@ export const IEventTypes = [ | ||||
|     FEATURE_STRATEGY_ADD, | ||||
|     FEATURE_STRATEGY_REMOVE, | ||||
|     FEATURE_TYPE_UPDATED, | ||||
|     FEATURE_COMPLETED, | ||||
|     FEATURE_UNCOMPLETED, | ||||
|     STRATEGY_ORDER_CHANGED, | ||||
|     DROP_FEATURE_TAGS, | ||||
|     FEATURE_UNTAGGED, | ||||
| @ -370,6 +373,7 @@ class BaseEvent implements IBaseEvent { | ||||
|     readonly createdByUserId: number; | ||||
| 
 | ||||
|     readonly ip: string; | ||||
| 
 | ||||
|     /** | ||||
|      * @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 | ||||
| @ -424,7 +428,11 @@ export class FeatureEnvironmentEvent extends BaseEvent { | ||||
|         this.environment = p.environment; | ||||
|     } | ||||
| } | ||||
| export type StrategyIds = { strategyIds: string[] }; | ||||
| 
 | ||||
| export type StrategyIds = { | ||||
|     strategyIds: string[]; | ||||
| }; | ||||
| 
 | ||||
| export class StrategiesOrderChangedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
| 
 | ||||
| @ -459,9 +467,13 @@ export class FeatureVariantEvent extends BaseEvent { | ||||
| 
 | ||||
|     readonly featureName: string; | ||||
| 
 | ||||
|     readonly data: { variants: IVariant[] }; | ||||
|     readonly data: { | ||||
|         variants: IVariant[]; | ||||
|     }; | ||||
| 
 | ||||
|     readonly preData: { variants: IVariant[] }; | ||||
|     readonly preData: { | ||||
|         variants: IVariant[]; | ||||
|     }; | ||||
| 
 | ||||
|     constructor(p: { | ||||
|         project: string; | ||||
| @ -485,9 +497,13 @@ export class EnvironmentVariantEvent extends BaseEvent { | ||||
| 
 | ||||
|     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 }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class ProjectCreatedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         project: string; | ||||
| @ -525,6 +543,7 @@ export class ProjectUpdatedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly data: any; | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         preData: any; | ||||
| @ -540,6 +559,7 @@ export class ProjectUpdatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class ProjectDeletedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         auditUser: IAuditUser; | ||||
| @ -552,6 +572,7 @@ export class ProjectDeletedEvent extends BaseEvent { | ||||
| export class RoleUpdatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -562,6 +583,7 @@ export class RoleUpdatedEvent extends BaseEvent { | ||||
|         this.preData = eventData.preData; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class FeatureChangeProjectEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
| 
 | ||||
| @ -582,7 +604,10 @@ export class FeatureChangeProjectEvent extends BaseEvent { | ||||
|         const { newProject, oldProject, featureName } = p; | ||||
|         this.project = newProject; | ||||
|         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 { | ||||
|     readonly data: any; | ||||
|     readonly featureName: string; | ||||
|     readonly project: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         featureName: string; | ||||
| @ -628,6 +680,7 @@ export class FeatureTaggedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     readonly featureName: string; | ||||
|     readonly project: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         featureName: string; | ||||
| @ -644,6 +697,7 @@ export class FeatureTaggedEvent extends BaseEvent { | ||||
| export class FeatureTypeUpdatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         preData: any; | ||||
| @ -654,10 +708,12 @@ export class FeatureTypeUpdatedEvent extends BaseEvent { | ||||
|         this.preData = eventData.preData; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class FeatureDependencyAddedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly featureName: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         featureName: string; | ||||
| @ -675,6 +731,7 @@ export class FeatureDependencyRemovedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly featureName: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         featureName: string; | ||||
| @ -687,6 +744,7 @@ export class FeatureDependencyRemovedEvent extends BaseEvent { | ||||
|         this.data = eventData.data; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class FeatureDependenciesRemovedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly featureName: string; | ||||
| @ -705,6 +763,7 @@ export class FeatureDependenciesRemovedEvent extends BaseEvent { | ||||
| export class FeaturesImportedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly environment: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         environment: string; | ||||
| @ -892,6 +951,7 @@ export class FeatureStrategyRemoveEvent extends BaseEvent { | ||||
| export class FeatureFavoritedEvent extends BaseEvent { | ||||
|     readonly featureName: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         featureName: string; | ||||
|         data: any; | ||||
| @ -906,6 +966,7 @@ export class FeatureFavoritedEvent extends BaseEvent { | ||||
| export class ProjectFavoritedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         data: any; | ||||
| @ -920,6 +981,7 @@ export class ProjectFavoritedEvent extends BaseEvent { | ||||
| export class FeatureUnfavoritedEvent extends BaseEvent { | ||||
|     readonly featureName: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         featureName: string; | ||||
|         data: any; | ||||
| @ -934,6 +996,7 @@ export class FeatureUnfavoritedEvent extends BaseEvent { | ||||
| export class ProjectUnfavoritedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         data: any; | ||||
| @ -1367,6 +1430,7 @@ export class UserDeletedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class TagTypeCreatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1378,6 +1442,7 @@ export class TagTypeCreatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class TagTypeDeletedEvent extends BaseEvent { | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         preData: any; | ||||
| @ -1389,6 +1454,7 @@ export class TagTypeDeletedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class TagTypeUpdatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1400,6 +1466,7 @@ export class TagTypeUpdatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class TagCreatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1411,6 +1478,7 @@ export class TagCreatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class TagDeletedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1422,6 +1490,7 @@ export class TagDeletedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class PatCreatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1430,8 +1499,10 @@ export class PatCreatedEvent extends BaseEvent { | ||||
|         this.data = eventData.data; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class PatDeletedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1444,6 +1515,7 @@ export class PatDeletedEvent extends BaseEvent { | ||||
| export class ProjectEnvironmentAdded extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly environment: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         environment: string; | ||||
| @ -1458,6 +1530,7 @@ export class ProjectEnvironmentAdded extends BaseEvent { | ||||
| export class ProjectEnvironmentRemoved extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly environment: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         environment: string; | ||||
| @ -1471,6 +1544,7 @@ export class ProjectEnvironmentRemoved extends BaseEvent { | ||||
| 
 | ||||
| export class FeaturesExportedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1482,6 +1556,7 @@ export class FeaturesExportedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class RoleCreatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         auditUser: IAuditUser; | ||||
| @ -1493,6 +1568,7 @@ export class RoleCreatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class RoleDeletedEvent extends BaseEvent { | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         preData: any; | ||||
|         auditUser: IAuditUser; | ||||
| @ -1501,8 +1577,10 @@ export class RoleDeletedEvent extends BaseEvent { | ||||
|         this.preData = eventData.preData; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class StrategyCreatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         auditUser: IAuditUser; | ||||
| @ -1511,8 +1589,10 @@ export class StrategyCreatedEvent extends BaseEvent { | ||||
|         this.data = eventData.data; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class StrategyUpdatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         auditUser: IAuditUser; | ||||
| @ -1521,8 +1601,10 @@ export class StrategyUpdatedEvent extends BaseEvent { | ||||
|         this.data = eventData.data; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class StrategyDeletedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         auditUser: IAuditUser; | ||||
| @ -1531,8 +1613,10 @@ export class StrategyDeletedEvent extends BaseEvent { | ||||
|         this.data = eventData.data; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class StrategyDeprecatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         auditUser: IAuditUser; | ||||
| @ -1541,8 +1625,10 @@ export class StrategyDeprecatedEvent extends BaseEvent { | ||||
|         this.data = eventData.data; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class StrategyReactivatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         auditUser: IAuditUser; | ||||
| @ -1557,6 +1643,7 @@ export class DefaultStrategyUpdatedEvent extends BaseEvent { | ||||
|     readonly environment: string; | ||||
|     readonly preData: any; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         project: string; | ||||
|         environment: string; | ||||
| @ -1574,6 +1661,7 @@ export class DefaultStrategyUpdatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class AddonConfigCreatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1586,6 +1674,7 @@ export class AddonConfigCreatedEvent extends BaseEvent { | ||||
| export class AddonConfigUpdatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         data: any; | ||||
| @ -1599,6 +1688,7 @@ export class AddonConfigUpdatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class AddonConfigDeletedEvent extends BaseEvent { | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         preData: any; | ||||
| @ -1611,6 +1701,7 @@ export class AddonConfigDeletedEvent extends BaseEvent { | ||||
| export class SegmentCreatedEvent extends BaseEvent { | ||||
|     readonly project: string; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         project: string; | ||||
| @ -1626,6 +1717,7 @@ export class SegmentUpdatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     readonly preData: any; | ||||
|     readonly project: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         project: string; | ||||
| @ -1638,9 +1730,11 @@ export class SegmentUpdatedEvent extends BaseEvent { | ||||
|         this.preData = eventData.preData; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class SegmentDeletedEvent extends BaseEvent { | ||||
|     readonly preData: any; | ||||
|     readonly project?: string; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|         preData: any; | ||||
| @ -1655,6 +1749,7 @@ export class SegmentDeletedEvent extends BaseEvent { | ||||
| export class GroupUpdatedEvent extends BaseEvent { | ||||
|     readonly preData: any; | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         data: any; | ||||
|         preData: any; | ||||
| @ -1668,6 +1763,7 @@ export class GroupUpdatedEvent extends BaseEvent { | ||||
| 
 | ||||
| export class GroupDeletedEvent extends BaseEvent { | ||||
|     readonly preData: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         preData: any; | ||||
|         auditUser: IAuditUser; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user