mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: revived feature goes to initial lifecycle stage (#6944)
This commit is contained in:
		
							parent
							
								
									675e1a9f8b
								
							
						
					
					
						commit
						8ed15165d2
					
				@ -35,6 +35,10 @@ export class FakeFeatureLifecycleStore implements IFeatureLifecycleStore {
 | 
				
			|||||||
        return this.lifecycles[feature] || [];
 | 
					        return this.lifecycles[feature] || [];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async delete(feature: string): Promise<void> {
 | 
				
			||||||
 | 
					        this.lifecycles[feature] = [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async stageExists(stage: FeatureLifecycleStage): Promise<boolean> {
 | 
					    async stageExists(stage: FeatureLifecycleStage): Promise<boolean> {
 | 
				
			||||||
        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));
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ import {
 | 
				
			|||||||
    FEATURE_ARCHIVED,
 | 
					    FEATURE_ARCHIVED,
 | 
				
			||||||
    FEATURE_COMPLETED,
 | 
					    FEATURE_COMPLETED,
 | 
				
			||||||
    FEATURE_CREATED,
 | 
					    FEATURE_CREATED,
 | 
				
			||||||
 | 
					    FEATURE_REVIVED,
 | 
				
			||||||
    type IEnvironment,
 | 
					    type IEnvironment,
 | 
				
			||||||
    type IUnleashConfig,
 | 
					    type IUnleashConfig,
 | 
				
			||||||
    type StageName,
 | 
					    type StageName,
 | 
				
			||||||
@ -82,6 +83,14 @@ test('can insert and read lifecycle stages', async () => {
 | 
				
			|||||||
        { stage: 'completed', enteredStageAt: expect.any(Date) },
 | 
					        { stage: 'completed', enteredStageAt: expect.any(Date) },
 | 
				
			||||||
        { stage: 'archived', enteredStageAt: expect.any(Date) },
 | 
					        { stage: 'archived', enteredStageAt: expect.any(Date) },
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eventStore.emit(FEATURE_REVIVED, { featureName });
 | 
				
			||||||
 | 
					    await reachedStage('initial');
 | 
				
			||||||
 | 
					    const initialLifecycle =
 | 
				
			||||||
 | 
					        await featureLifecycleService.getFeatureLifecycle(featureName);
 | 
				
			||||||
 | 
					    expect(initialLifecycle).toEqual([
 | 
				
			||||||
 | 
					        { stage: 'initial', enteredStageAt: expect.any(Date) },
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test('ignores lifecycle state updates when flag disabled', async () => {
 | 
					test('ignores lifecycle state updates when flag disabled', async () => {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ import {
 | 
				
			|||||||
    FEATURE_ARCHIVED,
 | 
					    FEATURE_ARCHIVED,
 | 
				
			||||||
    FEATURE_COMPLETED,
 | 
					    FEATURE_COMPLETED,
 | 
				
			||||||
    FEATURE_CREATED,
 | 
					    FEATURE_CREATED,
 | 
				
			||||||
 | 
					    FEATURE_REVIVED,
 | 
				
			||||||
    type IEnvironmentStore,
 | 
					    type IEnvironmentStore,
 | 
				
			||||||
    type IEventStore,
 | 
					    type IEventStore,
 | 
				
			||||||
    type IFlagResolver,
 | 
					    type IFlagResolver,
 | 
				
			||||||
@ -93,6 +94,11 @@ export class FeatureLifecycleService extends EventEmitter {
 | 
				
			|||||||
                this.featureArchived(event.featureName),
 | 
					                this.featureArchived(event.featureName),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        this.eventStore.on(FEATURE_REVIVED, async (event) => {
 | 
				
			||||||
 | 
					            await this.checkEnabled(() =>
 | 
				
			||||||
 | 
					                this.featureRevived(event.featureName),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getFeatureLifecycle(feature: string): Promise<FeatureLifecycleView> {
 | 
					    async getFeatureLifecycle(feature: string): Promise<FeatureLifecycleView> {
 | 
				
			||||||
@ -155,4 +161,9 @@ export class FeatureLifecycleService extends EventEmitter {
 | 
				
			|||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        this.emit(STAGE_ENTERED, { stage: 'archived' });
 | 
					        this.emit(STAGE_ENTERED, { stage: 'archived' });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async featureRevived(feature: string) {
 | 
				
			||||||
 | 
					        await this.featureLifecycleStore.delete(feature);
 | 
				
			||||||
 | 
					        await this.featureInitialized(feature);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,4 +11,5 @@ export interface IFeatureLifecycleStore {
 | 
				
			|||||||
    insert(featureLifecycleStages: FeatureLifecycleStage[]): Promise<void>;
 | 
					    insert(featureLifecycleStages: FeatureLifecycleStage[]): Promise<void>;
 | 
				
			||||||
    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>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -45,6 +45,10 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore {
 | 
				
			|||||||
        }));
 | 
					        }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async delete(feature: string): Promise<void> {
 | 
				
			||||||
 | 
					        await this.db('feature_lifecycles').where({ 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`,
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import {
 | 
				
			|||||||
    CLIENT_METRICS,
 | 
					    CLIENT_METRICS,
 | 
				
			||||||
    FEATURE_ARCHIVED,
 | 
					    FEATURE_ARCHIVED,
 | 
				
			||||||
    FEATURE_CREATED,
 | 
					    FEATURE_CREATED,
 | 
				
			||||||
 | 
					    FEATURE_REVIVED,
 | 
				
			||||||
    type IEventStore,
 | 
					    type IEventStore,
 | 
				
			||||||
    type StageName,
 | 
					    type StageName,
 | 
				
			||||||
} from '../../types';
 | 
					} from '../../types';
 | 
				
			||||||
@ -117,4 +118,7 @@ test('should return lifecycle stages', async () => {
 | 
				
			|||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await expectFeatureStage('archived');
 | 
					    await expectFeatureStage('archived');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eventStore.emit(FEATURE_REVIVED, { featureName: 'my_feature_a' });
 | 
				
			||||||
 | 
					    await reachedStage('initial');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user