mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: total count respect lifecycle filter (#9724)
This commit is contained in:
		
							parent
							
								
									827b8f274a
								
							
						
					
					
						commit
						e876e6438d
					
				@ -79,24 +79,6 @@ class FeatureSearchStore implements IFeatureSearchStore {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private getLatestLifecycleStageQuery() {
 | 
			
		||||
        return this.db('feature_lifecycles')
 | 
			
		||||
            .select(
 | 
			
		||||
                'feature as stage_feature',
 | 
			
		||||
                'stage as latest_stage',
 | 
			
		||||
                'status as stage_status',
 | 
			
		||||
                'created_at as entered_stage_at',
 | 
			
		||||
            )
 | 
			
		||||
            .distinctOn('stage_feature')
 | 
			
		||||
            .orderBy([
 | 
			
		||||
                'stage_feature',
 | 
			
		||||
                {
 | 
			
		||||
                    column: 'entered_stage_at',
 | 
			
		||||
                    order: 'desc',
 | 
			
		||||
                },
 | 
			
		||||
            ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async searchFeatures(
 | 
			
		||||
        {
 | 
			
		||||
            userId,
 | 
			
		||||
@ -147,6 +129,9 @@ class FeatureSearchStore implements IFeatureSearchStore {
 | 
			
		||||
                    'users.username as user_username',
 | 
			
		||||
                    'users.email as user_email',
 | 
			
		||||
                    'users.image_url as user_image_url',
 | 
			
		||||
                    'lifecycle.latest_stage',
 | 
			
		||||
                    'lifecycle.stage_status',
 | 
			
		||||
                    'lifecycle.entered_stage_at',
 | 
			
		||||
                ] as (string | Raw<any> | Knex.QueryBuilder)[];
 | 
			
		||||
 | 
			
		||||
                const lastSeenQuery = 'last_seen_at_metrics.last_seen_at';
 | 
			
		||||
@ -245,19 +230,48 @@ class FeatureSearchStore implements IFeatureSearchStore {
 | 
			
		||||
                        'users',
 | 
			
		||||
                        'users.id',
 | 
			
		||||
                        'features.created_by_user_id',
 | 
			
		||||
                    )
 | 
			
		||||
                    .leftJoin('last_seen_at_metrics', function () {
 | 
			
		||||
                        this.on(
 | 
			
		||||
                            'last_seen_at_metrics.environment',
 | 
			
		||||
                            '=',
 | 
			
		||||
                            'environments.name',
 | 
			
		||||
                        ).andOn(
 | 
			
		||||
                            'last_seen_at_metrics.feature_name',
 | 
			
		||||
                            '=',
 | 
			
		||||
                            'features.name',
 | 
			
		||||
                        );
 | 
			
		||||
                    })
 | 
			
		||||
                    .leftJoin(
 | 
			
		||||
                        this.db
 | 
			
		||||
                            .select(
 | 
			
		||||
                                'feature as stage_feature',
 | 
			
		||||
                                'stage as latest_stage',
 | 
			
		||||
                                'status as stage_status',
 | 
			
		||||
                                'created_at as entered_stage_at',
 | 
			
		||||
                            )
 | 
			
		||||
                            .from('feature_lifecycles')
 | 
			
		||||
                            .distinctOn('feature')
 | 
			
		||||
                            .orderBy([
 | 
			
		||||
                                'feature',
 | 
			
		||||
                                { column: 'created_at', order: 'desc' },
 | 
			
		||||
                            ])
 | 
			
		||||
                            .as('lifecycle'),
 | 
			
		||||
                        'features.name',
 | 
			
		||||
                        'lifecycle.stage_feature',
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                query.leftJoin('last_seen_at_metrics', function () {
 | 
			
		||||
                    this.on(
 | 
			
		||||
                        'last_seen_at_metrics.environment',
 | 
			
		||||
                        '=',
 | 
			
		||||
                        'environments.name',
 | 
			
		||||
                    ).andOn(
 | 
			
		||||
                        'last_seen_at_metrics.feature_name',
 | 
			
		||||
                        '=',
 | 
			
		||||
                        'features.name',
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
                if (this.flagResolver.isEnabled('flagsOverviewSearch')) {
 | 
			
		||||
                    const parsedLifecycle = lifecycle
 | 
			
		||||
                        ? parseSearchOperatorValue(
 | 
			
		||||
                              'lifecycle.latest_stage',
 | 
			
		||||
                              lifecycle,
 | 
			
		||||
                          )
 | 
			
		||||
                        : null;
 | 
			
		||||
                    if (parsedLifecycle) {
 | 
			
		||||
                        applyGenericQueryParams(query, [parsedLifecycle]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const rankingSql = this.buildRankingSql(
 | 
			
		||||
                    favoritesFirst,
 | 
			
		||||
@ -270,7 +284,6 @@ class FeatureSearchStore implements IFeatureSearchStore {
 | 
			
		||||
                    .select(selectColumns)
 | 
			
		||||
                    .denseRank('rank', this.db.raw(rankingSql));
 | 
			
		||||
            })
 | 
			
		||||
            .with('lifecycle', this.getLatestLifecycleStageQuery())
 | 
			
		||||
            .with(
 | 
			
		||||
                'final_ranks',
 | 
			
		||||
                this.db.raw(
 | 
			
		||||
@ -321,26 +334,8 @@ class FeatureSearchStore implements IFeatureSearchStore {
 | 
			
		||||
            .joinRaw('CROSS JOIN total_features')
 | 
			
		||||
            .whereBetween('final_rank', [offset + 1, offset + limit])
 | 
			
		||||
            .orderBy('final_rank');
 | 
			
		||||
        finalQuery
 | 
			
		||||
            .select(
 | 
			
		||||
                'lifecycle.latest_stage',
 | 
			
		||||
                'lifecycle.stage_status',
 | 
			
		||||
                'lifecycle.entered_stage_at',
 | 
			
		||||
            )
 | 
			
		||||
            .leftJoin(
 | 
			
		||||
                'lifecycle',
 | 
			
		||||
                'ranked_features.feature_name',
 | 
			
		||||
                'lifecycle.stage_feature',
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        if (this.flagResolver.isEnabled('flagsOverviewSearch')) {
 | 
			
		||||
            const parsedLifecycle = lifecycle
 | 
			
		||||
                ? parseSearchOperatorValue('lifecycle.latest_stage', lifecycle)
 | 
			
		||||
                : null;
 | 
			
		||||
            if (parsedLifecycle) {
 | 
			
		||||
                applyGenericQueryParams(finalQuery, [parsedLifecycle]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            finalQuery
 | 
			
		||||
                .leftJoin(
 | 
			
		||||
                    this.db('change_request_events AS cre')
 | 
			
		||||
 | 
			
		||||
@ -1111,6 +1111,10 @@ test('should return environment usage metrics and lifecycle', async () => {
 | 
			
		||||
        name: 'my_feature_b',
 | 
			
		||||
        createdAt: '2023-01-29T15:21:39.975Z',
 | 
			
		||||
    });
 | 
			
		||||
    await app.createFeature({
 | 
			
		||||
        name: 'my_feature_c',
 | 
			
		||||
        createdAt: '2023-01-29T15:21:39.975Z',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await stores.clientMetricsStoreV2.batchInsertMetrics([
 | 
			
		||||
        {
 | 
			
		||||
@ -1142,6 +1146,9 @@ test('should return environment usage metrics and lifecycle', async () => {
 | 
			
		||||
    await stores.featureLifecycleStore.insert([
 | 
			
		||||
        { feature: 'my_feature_b', stage: 'initial' },
 | 
			
		||||
    ]);
 | 
			
		||||
    await stores.featureLifecycleStore.insert([
 | 
			
		||||
        { feature: 'my_feature_c', stage: 'initial' },
 | 
			
		||||
    ]);
 | 
			
		||||
    await stores.featureLifecycleStore.insert([
 | 
			
		||||
        { feature: 'my_feature_b', stage: 'completed', status: 'discarded' },
 | 
			
		||||
    ]);
 | 
			
		||||
@ -1150,6 +1157,7 @@ test('should return environment usage metrics and lifecycle', async () => {
 | 
			
		||||
        query: 'my_feature_b',
 | 
			
		||||
    });
 | 
			
		||||
    expect(noExplicitLifecycle).toMatchObject({
 | 
			
		||||
        total: 1,
 | 
			
		||||
        features: [
 | 
			
		||||
            {
 | 
			
		||||
                name: 'my_feature_b',
 | 
			
		||||
@ -1180,14 +1188,17 @@ test('should return environment usage metrics and lifecycle', async () => {
 | 
			
		||||
            query: 'my_feature_b',
 | 
			
		||||
            lifecycle: 'IS:initial',
 | 
			
		||||
        });
 | 
			
		||||
    expect(noFeaturesWithOtherLifecycle).toMatchObject({ features: [] });
 | 
			
		||||
    expect(noFeaturesWithOtherLifecycle).toMatchObject({
 | 
			
		||||
        total: 0,
 | 
			
		||||
        features: [],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const { body: featureWithMatchingLifecycle } =
 | 
			
		||||
        await searchFeaturesWithLifecycle({
 | 
			
		||||
            query: 'my_feature_b',
 | 
			
		||||
            lifecycle: 'IS:completed',
 | 
			
		||||
        });
 | 
			
		||||
    expect(featureWithMatchingLifecycle).toMatchObject({
 | 
			
		||||
        total: 1,
 | 
			
		||||
        features: [{ name: 'my_feature_b' }],
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user