diff --git a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx index c4320494118d..9ae5d4b0f293 100644 --- a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx +++ b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx @@ -92,6 +92,7 @@ export const ProjectFeatureToggles = ({ type: tableState.type, state: tableState.state, createdBy: tableState.createdBy, + archived: tableState.archived, }; const { favorite, unfavorite } = useFavoriteFeaturesApi(); diff --git a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts index 500a02e23410..29976c741f53 100644 --- a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts +++ b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts @@ -40,6 +40,7 @@ export const useProjectFeatureSearch = ( createdAt: FilterItemParam, type: FilterItemParam, createdBy: FilterItemParam, + archived: FilterItemParam, }; const [tableState, setTableState] = usePersistentTableState( `${storageKey}-${projectId}`, diff --git a/src/lib/features/feature-search/feature-search-controller.ts b/src/lib/features/feature-search/feature-search-controller.ts index a29889cfdffb..a82ca6bde275 100644 --- a/src/lib/features/feature-search/feature-search-controller.ts +++ b/src/lib/features/feature-search/feature-search-controller.ts @@ -102,6 +102,7 @@ export default class FeatureSearchController extends Controller { state, status, favoritesFirst, + archived, sortBy, } = req.query; const userId = req.user.id; @@ -131,6 +132,7 @@ export default class FeatureSearchController extends Controller { ['enabled', 'disabled'].includes(tag[1]), ); const normalizedFavoritesFirst = favoritesFirst === 'true'; + const normalizedArchived = archived === 'IS:true'; const { features, total } = await this.featureSearchService.search({ searchParams: normalizedQuery, project, @@ -147,6 +149,7 @@ export default class FeatureSearchController extends Controller { limit: normalizedLimit, sortOrder: normalizedSortOrder, favoritesFirst: normalizedFavoritesFirst, + archived: normalizedArchived, }); this.openApiService.respondWithValidation( diff --git a/src/lib/features/feature-search/feature-search-store.ts b/src/lib/features/feature-search/feature-search-store.ts index 76bfc84deb2b..bf3fd577c1a7 100644 --- a/src/lib/features/feature-search/feature-search-store.ts +++ b/src/lib/features/feature-search/feature-search-store.ts @@ -101,6 +101,7 @@ class FeatureSearchStore implements IFeatureSearchStore { limit, sortOrder, sortBy, + archived, favoritesFirst, }: IFeatureSearchParams, queryParams: IQueryParam[], @@ -188,9 +189,8 @@ class FeatureSearchStore implements IFeatureSearchStore { } }); } - query - .modify(FeatureToggleStore.filterByArchived, false) + .modify(FeatureToggleStore.filterByArchived, archived) .leftJoin( 'feature_environments', 'feature_environments.feature_name', diff --git a/src/lib/features/feature-search/feature.search.e2e.test.ts b/src/lib/features/feature-search/feature.search.e2e.test.ts index 34f116daef5c..396010cc2278 100644 --- a/src/lib/features/feature-search/feature.search.e2e.test.ts +++ b/src/lib/features/feature-search/feature.search.e2e.test.ts @@ -57,16 +57,23 @@ afterAll(async () => { }); beforeEach(async () => { + await db.stores.dependentFeaturesStore.deleteAll(); await db.stores.featureToggleStore.deleteAll(); await db.stores.segmentStore.deleteAll(); }); const searchFeatures = async ( - { query = '', project = 'IS:default' }: FeatureSearchQueryParameters, + { + query = '', + project = 'IS:default', + archived = 'IS:false', + }: FeatureSearchQueryParameters, expectedCode = 200, ) => { return app.request - .get(`/api/admin/search/features?query=${query}&project=${project}`) + .get( + `/api/admin/search/features?query=${query}&project=${project}&archived=${archived}`, + ) .expect(expectedCode); }; @@ -1128,3 +1135,38 @@ test('should return dependencyType', async () => { ], }); }); + +test('should return archived when query param set', async () => { + await app.createFeature({ + name: 'my_feature_a', + createdAt: '2023-01-29T15:21:39.975Z', + }); + await app.createFeature({ + name: 'my_feature_b', + createdAt: '2023-01-29T15:21:39.975Z', + archived: true, + }); + + const { body } = await searchFeatures({ + query: 'my_feature', + }); + expect(body).toMatchObject({ + features: [ + { + name: 'my_feature_a', + }, + ], + }); + + const { body: archivedFeatures } = await searchFeatures({ + query: 'my_feature', + archived: 'IS:true', + }); + expect(archivedFeatures).toMatchObject({ + features: [ + { + name: 'my_feature_b', + }, + ], + }); +}); diff --git a/src/lib/features/feature-toggle/types/feature-toggle-strategies-store-type.ts b/src/lib/features/feature-toggle/types/feature-toggle-strategies-store-type.ts index 6e9cc96b295a..6998dc1d0215 100644 --- a/src/lib/features/feature-toggle/types/feature-toggle-strategies-store-type.ts +++ b/src/lib/features/feature-toggle/types/feature-toggle-strategies-store-type.ts @@ -33,6 +33,7 @@ export interface IFeatureSearchParams { status?: string[][]; offset: number; favoritesFirst?: boolean; + archived?: boolean; limit: number; sortBy?: string; sortOrder: 'asc' | 'desc'; diff --git a/src/lib/openapi/spec/feature-search-query-parameters.ts b/src/lib/openapi/spec/feature-search-query-parameters.ts index 0de66e97ec88..c360331475e6 100644 --- a/src/lib/openapi/spec/feature-search-query-parameters.ts +++ b/src/lib/openapi/spec/feature-search-query-parameters.ts @@ -146,6 +146,16 @@ export const featureSearchQueryParameters = [ 'The flag to indicate if the favorite features should be returned first. By default it is set to false.', in: 'query', }, + { + name: 'archived', + schema: { + type: 'string', + example: 'IS:true', + }, + description: + 'Whether to get results for archived feature flags or active feature flags. If `true`, Unleash will return only archived flags. If `false`, it will return only active flags.', + in: 'query', + }, { name: 'createdAt', schema: {