From 0429c1985af9f3d5a262ef540d4671462cc119ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Tue, 13 May 2025 12:09:52 +0100 Subject: [PATCH] refactor: remove deprecated get archive featured by project endpoint (#9938) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://linear.app/unleash/issue/2-3367/remove-get-apiadminarchivefeaturesprojectid-deprecated-in-4110 Removes GET `/api/admin/archive/features/{projectId}` which was deprecated in v4.11. Also cleans up related code. I leveraged our search features endpoint where needed, since that's what we're using now in our UI (to see archived features you filter by archived=true). Builds on top of https://github.com/Unleash/unleash/pull/9924 — Since they're both related. --- .../ArchiveTable/ArchiveBatchActions.tsx | 4 - .../ArchiveTable/ArchiveTable.test.tsx | 160 --------- .../archive/ArchiveTable/ArchiveTable.tsx | 320 ------------------ .../FeatureArchivedCell.tsx | 45 --- .../archive/ProjectFeaturesArchiveTable.tsx | 35 -- .../src/component/archive/RedirectArchive.tsx | 7 - .../FeatureNotFound/FeatureNotFound.tsx | 7 +- .../__snapshots__/routes.test.tsx.snap | 8 - frontend/src/component/menu/routes.ts | 9 - .../src/component/project/Project/Project.tsx | 2 - .../ProjectFeaturesArchive.tsx | 12 - .../useFeaturesArchive/useFeaturesArchive.ts | 27 -- .../openapi/models/archivedFeatureSchema.ts | 42 --- .../archivedFeatureSchemaEnvironmentsItem.ts | 17 - .../openapi/models/archivedFeaturesSchema.ts | 16 - .../openapi/models/getArchivedFeatures401.ts | 14 - .../openapi/models/getArchivedFeatures403.ts | 14 - .../getArchivedFeaturesByProjectId401.ts | 14 - .../getArchivedFeaturesByProjectId403.ts | 14 - frontend/src/openapi/models/index.ts | 7 - .../feature-search/feature.search.e2e.test.ts | 4 +- .../archive-feature-toggle-controller.ts | 47 +-- .../feature-toggle-row-converter.ts | 26 -- .../fakes/fake-feature-toggle-store.ts | 4 - .../feature-toggle/feature-toggle-service.ts | 6 - .../feature-toggle/feature-toggle-store.ts | 26 -- .../tests/archive-feature-toggles.e2e.test.ts | 25 +- .../feature-toggle-last-seen-at.e2e.test.ts | 22 -- .../tests/feature-toggles.e2e.test.ts | 4 +- .../types/feature-toggle-store-type.ts | 2 - .../openapi/spec/archived-feature-schema.ts | 102 ------ .../openapi/spec/archived-features-schema.ts | 30 -- src/lib/openapi/spec/index.ts | 2 - .../e2e/api/admin/feature-archive.e2e.test.ts | 12 +- 34 files changed, 38 insertions(+), 1048 deletions(-) delete mode 100644 frontend/src/component/archive/ArchiveTable/ArchiveTable.test.tsx delete mode 100644 frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx delete mode 100644 frontend/src/component/archive/ArchiveTable/FeatureArchivedCell/FeatureArchivedCell.tsx delete mode 100644 frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx delete mode 100644 frontend/src/component/archive/RedirectArchive.tsx delete mode 100644 frontend/src/component/project/Project/ProjectFeaturesArchive/ProjectFeaturesArchive.tsx delete mode 100644 frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts delete mode 100644 frontend/src/openapi/models/archivedFeatureSchema.ts delete mode 100644 frontend/src/openapi/models/archivedFeatureSchemaEnvironmentsItem.ts delete mode 100644 frontend/src/openapi/models/archivedFeaturesSchema.ts delete mode 100644 frontend/src/openapi/models/getArchivedFeatures401.ts delete mode 100644 frontend/src/openapi/models/getArchivedFeatures403.ts delete mode 100644 frontend/src/openapi/models/getArchivedFeaturesByProjectId401.ts delete mode 100644 frontend/src/openapi/models/getArchivedFeaturesByProjectId403.ts delete mode 100644 src/lib/openapi/spec/archived-feature-schema.ts delete mode 100644 src/lib/openapi/spec/archived-features-schema.ts diff --git a/frontend/src/component/archive/ArchiveTable/ArchiveBatchActions.tsx b/frontend/src/component/archive/ArchiveTable/ArchiveBatchActions.tsx index 2513f23ca1..eab3cd30ab 100644 --- a/frontend/src/component/archive/ArchiveTable/ArchiveBatchActions.tsx +++ b/frontend/src/component/archive/ArchiveTable/ArchiveBatchActions.tsx @@ -7,7 +7,6 @@ import { UPDATE_FEATURE, } from 'component/providers/AccessProvider/permissions'; import { PermissionHOC } from 'component/common/PermissionHOC/PermissionHOC'; -import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive'; import { ArchivedFeatureDeleteConfirm } from './ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { ArchivedFeatureReviveConfirm } from './ArchivedFeatureActionCell/ArchivedFeatureReviveConfirm/ArchivedFeatureReviveConfirm'; @@ -23,7 +22,6 @@ export const ArchiveBatchActions: FC = ({ projectId, onConfirm, }) => { - const { refetchArchived } = useFeaturesArchive(projectId); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [reviveModalOpen, setReviveModalOpen] = useState(false); const { trackEvent } = usePlausibleTracker(); @@ -70,7 +68,6 @@ export const ArchiveBatchActions: FC = ({ open={deleteModalOpen} setOpen={setDeleteModalOpen} refetch={() => { - refetchArchived(); onConfirm?.(); trackEvent('batch_operations', { props: { @@ -85,7 +82,6 @@ export const ArchiveBatchActions: FC = ({ open={reviveModalOpen} setOpen={setReviveModalOpen} refetch={() => { - refetchArchived(); onConfirm?.(); trackEvent('batch_operations', { props: { diff --git a/frontend/src/component/archive/ArchiveTable/ArchiveTable.test.tsx b/frontend/src/component/archive/ArchiveTable/ArchiveTable.test.tsx deleted file mode 100644 index 010d4ee45a..0000000000 --- a/frontend/src/component/archive/ArchiveTable/ArchiveTable.test.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { ArchiveTable } from './ArchiveTable'; -import { render } from 'utils/testRenderer'; -import { useState } from 'react'; -import { screen, fireEvent, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { - DELETE_FEATURE, - UPDATE_FEATURE, -} from 'component/providers/AccessProvider/permissions'; -import ToastRenderer from 'component/common/ToastRenderer/ToastRenderer'; -import { testServerRoute, testServerSetup } from 'utils/testServer'; - -const mockedFeatures = [ - { - name: 'someFeature', - description: '', - type: 'release', - project: 'default', - stale: false, - createdAt: '2023-08-10T09:28:58.928Z', - lastSeenAt: null, - impressionData: false, - archivedAt: '2023-08-11T10:18:03.429Z', - archived: true, - }, - { - name: 'someOtherFeature', - description: '', - type: 'release', - project: 'default', - stale: false, - createdAt: '2023-08-10T09:28:58.928Z', - lastSeenAt: null, - impressionData: false, - archivedAt: '2023-08-11T10:18:03.429Z', - archived: true, - }, -]; - -const Component = () => { - const [storedParams, setStoredParams] = useState({}); - return ( - Promise.resolve({})} - loading={false} - setStoredParams={setStoredParams as any} - storedParams={storedParams as any} - projectId='default' - /> - ); -}; - -const server = testServerSetup(); - -const setupApi = () => { - testServerRoute( - server, - '/api/admin/projects/default/revive', - {}, - 'post', - 200, - ); - - testServerRoute(server, '/api/admin/projects/default/overview', { - environment: 'Open Source', - }); - testServerRoute(server, '/api/admin/ui-config', { - archivedAt: null, - }); -}; - -test('should load the table', async () => { - render(, { permissions: [{ permission: UPDATE_FEATURE }] }); - expect(screen.getByRole('table')).toBeInTheDocument(); - - await screen.findByText('someFeature'); -}); - -test('should show confirm dialog when reviving flag', async () => { - setupApi(); - render( - <> - - - , - { permissions: [{ permission: UPDATE_FEATURE }] }, - ); - await screen.findByText('someFeature'); - - const reviveButton = screen.getAllByTestId( - 'revive-feature-flag-button', - )?.[0]; - fireEvent.click(reviveButton); - - await screen.findByText('Revive feature flag?'); - const reviveFlagsButton = screen.getByRole('button', { - name: /Revive feature flag/i, - }); - await waitFor(async () => { - expect(reviveFlagsButton).toBeEnabled(); - }); - fireEvent.click(reviveFlagsButton); - - await screen.findByText('Feature flags revived'); -}); - -test('should show confirm dialog when batch reviving flag', async () => { - setupApi(); - render( - <> - - - , - { - permissions: [ - { permission: UPDATE_FEATURE, project: 'default' }, - { permission: DELETE_FEATURE, project: 'default' }, - ], - }, - ); - await screen.findByText('someFeature'); - - const selectAll = await screen.findByTestId('select_all_rows'); - fireEvent.click(selectAll.firstChild!); - const batchReviveButton = await screen.findByText(/Revive/i); - await userEvent.click(batchReviveButton!); - - await screen.findByText('Revive feature flags?'); - - const reviveTogglesButton = screen.getByRole('button', { - name: /Revive feature flags/i, - }); - fireEvent.click(reviveTogglesButton); - - await screen.findByText('Feature flags revived'); -}); - -test('should show info box when disableAllEnvsOnRevive flag is on', async () => { - setupApi(); - render( - <> - - - , - { permissions: [{ permission: UPDATE_FEATURE }] }, - ); - await screen.findByText('someFeature'); - - const reviveButton = screen.getAllByTestId( - 'revive-feature-flag-button', - )?.[0]; - fireEvent.click(reviveButton); - - await screen.findByText('Revive feature flag?'); - await screen.findByText( - 'Revived feature flags will be automatically disabled in all environments', - ); -}); diff --git a/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx b/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx deleted file mode 100644 index 6800488d18..0000000000 --- a/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx +++ /dev/null @@ -1,320 +0,0 @@ -import { PageContent } from 'component/common/PageContent/PageContent'; -import { PageHeader } from 'component/common/PageHeader/PageHeader'; -import { TablePlaceholder, VirtualizedTable } from 'component/common/Table'; -import { - type SortingRule, - useFlexLayout, - useRowSelect, - useSortBy, - useTable, -} from 'react-table'; -import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; -import { Checkbox, useMediaQuery } from '@mui/material'; -import { sortTypes } from 'utils/sortTypes'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; -import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { Search } from 'component/common/Search/Search'; -import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/FeatureTypeCell'; -import { ArchivedFeatureActionCell } from 'component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureActionCell'; -import { featuresPlaceholder } from 'component/feature/FeatureToggleList/FeatureToggleListTable'; -import theme from 'themes/theme'; -import type { ArchivedFeatureSchema } from 'openapi'; -import { useSearch } from 'hooks/useSearch'; -import { FeatureArchivedCell } from './FeatureArchivedCell/FeatureArchivedCell'; -import { useSearchParams } from 'react-router-dom'; -import { ArchivedFeatureDeleteConfirm } from './ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm'; -import type { IFeatureToggle } from 'interfaces/featureToggle'; -import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; -import { RowSelectCell } from '../../project/Project/ProjectFeatureToggles/RowSelectCell/RowSelectCell'; -import { BatchSelectionActionsBar } from '../../common/BatchSelectionActionsBar/BatchSelectionActionsBar'; -import { ArchiveBatchActions } from './ArchiveBatchActions'; -import { FeatureEnvironmentSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureEnvironmentSeenCell'; -import { ArchivedFeatureReviveConfirm } from './ArchivedFeatureActionCell/ArchivedFeatureReviveConfirm/ArchivedFeatureReviveConfirm'; - -export interface IFeaturesArchiveTableProps { - archivedFeatures: ArchivedFeatureSchema[]; - title: string; - refetch: () => void; - loading: boolean; - storedParams: SortingRule; - setStoredParams: ( - newValue: - | SortingRule - | ((prev: SortingRule) => SortingRule), - ) => SortingRule; - projectId: string; -} - -export const ArchiveTable = ({ - archivedFeatures = [], - loading, - refetch, - storedParams, - setStoredParams, - title, - projectId, -}: IFeaturesArchiveTableProps) => { - const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); - const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg')); - const [deleteModalOpen, setDeleteModalOpen] = useState(false); - const [deletedFeature, setDeletedFeature] = useState(); - - const [reviveModalOpen, setReviveModalOpen] = useState(false); - const [revivedFeature, setRevivedFeature] = useState(); - - const [searchParams, setSearchParams] = useSearchParams(); - - const [searchValue, setSearchValue] = useState( - searchParams.get('search') || '', - ); - - const columns = useMemo( - () => [ - { - id: 'Select', - Header: ({ getToggleAllRowsSelectedProps }: any) => ( - - ), - Cell: ({ row }: any) => ( - - ), - maxWidth: 50, - disableSortBy: true, - hideInMenu: true, - }, - { - Header: 'Seen', - accessor: 'lastSeenAt', - Cell: ({ row: { original: feature } }: any) => { - return ; - }, - align: 'center', - maxWidth: 80, - }, - { - Header: 'Type', - accessor: 'type', - width: 85, - canSort: true, - Cell: FeatureTypeCell, - align: 'center', - }, - { - Header: 'Name', - accessor: 'name', - searchable: true, - minWidth: 100, - Cell: ({ value, row: { original } }: any) => ( - - ), - sortType: 'alphanumeric', - }, - { - Header: 'Created', - accessor: 'createdAt', - width: 150, - Cell: DateCell, - }, - { - Header: 'Archived', - accessor: 'archivedAt', - width: 150, - Cell: FeatureArchivedCell, - }, - { - Header: 'Actions', - id: 'Actions', - align: 'center', - maxWidth: 120, - canSort: false, - Cell: ({ row: { original: feature } }: any) => ( - { - setRevivedFeature(feature); - setReviveModalOpen(true); - }} - onDelete={() => { - setDeletedFeature(feature); - setDeleteModalOpen(true); - }} - /> - ), - }, - // Always hidden -- for search - { - accessor: 'description', - header: 'Description', - searchable: true, - }, - ], - [], - ); - - const { - data: searchedData, - getSearchText, - getSearchContext, - } = useSearch(columns, searchValue, archivedFeatures); - - const data = useMemo( - () => (loading ? featuresPlaceholder : searchedData), - [searchedData, loading], - ); - - const [initialState] = useState(() => ({ - sortBy: [ - { - id: searchParams.get('sort') || storedParams.id, - desc: searchParams.has('order') - ? searchParams.get('order') === 'desc' - : storedParams.desc, - }, - ], - hiddenColumns: ['description'], - selectedRowIds: {}, - })); - - const getRowId = useCallback((row: any) => row.name, []); - - const { - headerGroups, - rows, - state: { sortBy, selectedRowIds }, - prepareRow, - setHiddenColumns, - toggleAllRowsSelected, - } = useTable( - { - columns: columns as any[], // TODO: fix after `react-table` v8 update - data, - initialState, - sortTypes, - autoResetHiddenColumns: false, - autoResetSelectedRows: false, - disableSortRemove: true, - autoResetSortBy: false, - getRowId, - }, - useFlexLayout, - useSortBy, - useRowSelect, - ); - - useConditionallyHiddenColumns( - [ - { - condition: isSmallScreen, - columns: ['type', 'createdAt'], - }, - { - condition: isMediumScreen, - columns: ['lastSeenAt', 'stale'], - }, - ], - setHiddenColumns, - columns, - ); - - useEffect(() => { - if (loading) { - return; - } - const tableState: Record = {}; - tableState.sort = sortBy[0].id; - if (sortBy[0].desc) { - tableState.order = 'desc'; - } - if (searchValue) { - tableState.search = searchValue; - } - - setSearchParams(tableState, { - replace: true, - }); - setStoredParams({ id: sortBy[0].id, desc: sortBy[0].desc || false }); - }, [loading, sortBy, searchValue]); - - return ( - <> - - } - /> - } - > - - - - ( - 0} - show={ - - No feature flags found matching “ - {searchValue}” - - } - elseShow={ - - None of the feature flags were archived yet. - - } - /> - )} - /> - - - - - toggleAllRowsSelected(false)} - /> - - - ); -}; diff --git a/frontend/src/component/archive/ArchiveTable/FeatureArchivedCell/FeatureArchivedCell.tsx b/frontend/src/component/archive/ArchiveTable/FeatureArchivedCell/FeatureArchivedCell.tsx deleted file mode 100644 index 16f97c3e27..0000000000 --- a/frontend/src/component/archive/ArchiveTable/FeatureArchivedCell/FeatureArchivedCell.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import type { FC } from 'react'; -import { TimeAgo } from 'component/common/TimeAgo/TimeAgo'; -import { Tooltip, Typography, useTheme } from '@mui/material'; -import { formatDateYMD } from 'utils/formatDate'; -import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; -import { useLocationSettings } from 'hooks/useLocationSettings'; - -interface IFeatureArchivedCellProps { - value?: string | Date | null; -} - -export const FeatureArchivedCell: FC = ({ - value: archivedAt, -}) => { - const { locationSettings } = useLocationSettings(); - const theme = useTheme(); - - if (!archivedAt) - return ( - - - not available - - - ); - - return ( - - - - - - - - ); -}; diff --git a/frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx b/frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx deleted file mode 100644 index bec8e743ad..0000000000 --- a/frontend/src/component/archive/ProjectFeaturesArchiveTable.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive'; -import type { FC } from 'react'; -import type { SortingRule } from 'react-table'; -import { createLocalStorage } from 'utils/createLocalStorage'; -import { ArchiveTable } from './ArchiveTable/ArchiveTable'; - -const defaultSort: SortingRule = { id: 'archivedAt' }; - -interface IProjectFeaturesTable { - projectId: string; -} - -export const ProjectFeaturesArchiveTable: FC = ({ - projectId, -}) => { - const { archivedFeatures, loading, refetchArchived } = - useFeaturesArchive(projectId); - - const { value, setValue } = createLocalStorage( - `${projectId}:ProjectFeaturesArchiveTable`, - defaultSort, - ); - - return ( - - ); -}; diff --git a/frontend/src/component/archive/RedirectArchive.tsx b/frontend/src/component/archive/RedirectArchive.tsx deleted file mode 100644 index 54a1dda080..0000000000 --- a/frontend/src/component/archive/RedirectArchive.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { Navigate } from 'react-router-dom'; - -const RedirectArchive = () => { - return ; -}; - -export default RedirectArchive; diff --git a/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx b/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx index 49b4bb2197..1523488acd 100644 --- a/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx @@ -1,8 +1,8 @@ import { Link } from 'react-router-dom'; import { getCreateTogglePath } from 'utils/routePathHelpers'; -import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { styled } from '@mui/material'; +import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch'; const StyledFeatureId = styled('strong')({ wordBreak: 'break-all', @@ -11,7 +11,10 @@ const StyledFeatureId = styled('strong')({ export const FeatureNotFound = () => { const projectId = useRequiredPathParam('projectId'); const featureId = useRequiredPathParam('featureId'); - const { archivedFeatures } = useFeaturesArchive(projectId); + const { features: archivedFeatures } = useFeatureSearch({ + project: `IS:${projectId}`, + archived: 'IS:true', + }); const createFeatureTogglePath = getCreateTogglePath(projectId, { name: featureId, diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap index 57bb8cac40..c538c2267b 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -35,14 +35,6 @@ exports[`returns all baseRoutes 1`] = ` "title": "Create", "type": "protected", }, - { - "component": [Function], - "menu": {}, - "parent": "/archive", - "path": "/projects/:projectId/archived", - "title": ":projectId", - "type": "protected", - }, { "component": [Function], "menu": {}, diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index 510f5c83f5..efb9e21c20 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -10,7 +10,6 @@ import ResetPassword from 'component/user/ResetPassword/ResetPassword'; import ForgottenPassword from 'component/user/ForgottenPassword/ForgottenPassword'; import { ProjectList } from 'component/project/ProjectList/ProjectList'; import { ArchiveProjectList } from 'component/project/ProjectList/ArchiveProjectList'; -import RedirectArchive from 'component/archive/RedirectArchive'; import CreateEnvironment from 'component/environments/CreateEnvironment/CreateEnvironment'; import EditEnvironment from 'component/environments/EditEnvironment/EditEnvironment'; import { EditContext } from 'component/context/EditContext/EditContext'; @@ -80,14 +79,6 @@ export const routes: IRoute[] = [ enterprise: true, menu: {}, }, - { - path: '/projects/:projectId/archived', - title: ':projectId', - parent: '/archive', - component: RedirectArchive, - type: 'protected', - menu: {}, - }, { path: '/projects/:projectId/features/:featureId/copy', parent: '/projects/:projectId/features/:featureId/', diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx index a0e34bbddd..039d776235 100644 --- a/frontend/src/component/project/Project/Project.tsx +++ b/frontend/src/component/project/Project/Project.tsx @@ -27,7 +27,6 @@ import useToast from 'hooks/useToast'; import useQueryParams from 'hooks/useQueryParams'; import { useEffect, useState, type ReactNode } from 'react'; import ProjectEnvironment from '../ProjectEnvironment/ProjectEnvironment'; -import { ProjectFeaturesArchive } from './ProjectFeaturesArchive/ProjectFeaturesArchive'; import ProjectFlags from './ProjectFlags'; import ProjectHealth from './ProjectHealth/ProjectHealth'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; @@ -387,7 +386,6 @@ export const Project = () => { } /> } /> - } /> } /> } /> { - const projectId = useRequiredPathParam('projectId'); - const projectName = useProjectOverviewNameOrId(projectId); - usePageTitle(`Project archived flags – ${projectName}`); - - return ; -}; diff --git a/frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts b/frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts deleted file mode 100644 index fe52e806ae..0000000000 --- a/frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts +++ /dev/null @@ -1,27 +0,0 @@ -import useSWR from 'swr'; -import type { ArchivedFeaturesSchema } from 'openapi'; -import handleErrorResponses from '../httpErrorResponseHandler'; -import { formatApiPath } from 'utils/formatPath'; - -const fetcher = (path: string) => { - return fetch(path) - .then(handleErrorResponses('Feature flag archive')) - .then((res) => res.json()); -}; - -export const useFeaturesArchive = (projectId: string) => { - const { data, error, mutate, isLoading } = useSWR( - formatApiPath(`/api/admin/archive/features/${projectId}`), - fetcher, - { - refreshInterval: 15 * 1000, // ms - }, - ); - - return { - archivedFeatures: data?.features, - refetchArchived: mutate, - loading: isLoading, - error, - }; -}; diff --git a/frontend/src/openapi/models/archivedFeatureSchema.ts b/frontend/src/openapi/models/archivedFeatureSchema.ts deleted file mode 100644 index c255a0bb93..0000000000 --- a/frontend/src/openapi/models/archivedFeatureSchema.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ -import type { ArchivedFeatureSchemaEnvironmentsItem } from './archivedFeatureSchemaEnvironmentsItem'; - -/** - * An archived project feature flag definition - */ -export interface ArchivedFeatureSchema { - /** The date the feature was archived */ - archivedAt?: string; - /** The date the feature was created */ - createdAt?: string; - /** - * Detailed description of the feature - * @nullable - */ - description?: string | null; - /** - * The list of environments where the feature can be used - * @deprecated - */ - environments?: ArchivedFeatureSchemaEnvironmentsItem[]; - /** `true` if the impression data collection is enabled for the feature, otherwise `false`. */ - impressionData?: boolean; - /** - * The date when metrics where last collected for the feature. This field was deprecated in v5, use the one in featureEnvironmentSchema - * @deprecated - * @nullable - */ - lastSeenAt?: string | null; - /** Unique feature name */ - name: string; - /** Name of the project the feature belongs to */ - project: string; - /** `true` if the feature is stale based on the age and feature type, otherwise `false`. */ - stale?: boolean; - /** Type of the flag e.g. experiment, kill-switch, release, operational, permission */ - type?: string; -} diff --git a/frontend/src/openapi/models/archivedFeatureSchemaEnvironmentsItem.ts b/frontend/src/openapi/models/archivedFeatureSchemaEnvironmentsItem.ts deleted file mode 100644 index 1bc9447f8b..0000000000 --- a/frontend/src/openapi/models/archivedFeatureSchemaEnvironmentsItem.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ - -export type ArchivedFeatureSchemaEnvironmentsItem = { - /** `true` if the feature is enabled for the environment, otherwise `false`. */ - enabled?: boolean; - /** - * The date when metrics where last collected for the feature environment - * @nullable - */ - lastSeenAt?: string | null; - /** The name of the environment */ - name?: string; -}; diff --git a/frontend/src/openapi/models/archivedFeaturesSchema.ts b/frontend/src/openapi/models/archivedFeaturesSchema.ts deleted file mode 100644 index 75545207ed..0000000000 --- a/frontend/src/openapi/models/archivedFeaturesSchema.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ -import type { ArchivedFeatureSchema } from './archivedFeatureSchema'; - -/** - * A list of archived features - */ -export interface ArchivedFeaturesSchema { - /** A list of features */ - features: ArchivedFeatureSchema[]; - /** The version of the feature's schema */ - version: number; -} diff --git a/frontend/src/openapi/models/getArchivedFeatures401.ts b/frontend/src/openapi/models/getArchivedFeatures401.ts deleted file mode 100644 index 8c9694169c..0000000000 --- a/frontend/src/openapi/models/getArchivedFeatures401.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ - -export type GetArchivedFeatures401 = { - /** The ID of the error instance */ - id?: string; - /** A description of what went wrong. */ - message?: string; - /** The name of the error kind */ - name?: string; -}; diff --git a/frontend/src/openapi/models/getArchivedFeatures403.ts b/frontend/src/openapi/models/getArchivedFeatures403.ts deleted file mode 100644 index 1ebec4e331..0000000000 --- a/frontend/src/openapi/models/getArchivedFeatures403.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ - -export type GetArchivedFeatures403 = { - /** The ID of the error instance */ - id?: string; - /** A description of what went wrong. */ - message?: string; - /** The name of the error kind */ - name?: string; -}; diff --git a/frontend/src/openapi/models/getArchivedFeaturesByProjectId401.ts b/frontend/src/openapi/models/getArchivedFeaturesByProjectId401.ts deleted file mode 100644 index b19ca8d9d9..0000000000 --- a/frontend/src/openapi/models/getArchivedFeaturesByProjectId401.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ - -export type GetArchivedFeaturesByProjectId401 = { - /** The ID of the error instance */ - id?: string; - /** A description of what went wrong. */ - message?: string; - /** The name of the error kind */ - name?: string; -}; diff --git a/frontend/src/openapi/models/getArchivedFeaturesByProjectId403.ts b/frontend/src/openapi/models/getArchivedFeaturesByProjectId403.ts deleted file mode 100644 index 4feeaf4a3e..0000000000 --- a/frontend/src/openapi/models/getArchivedFeaturesByProjectId403.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Generated by Orval - * Do not edit manually. - * See `gen:api` script in package.json - */ - -export type GetArchivedFeaturesByProjectId403 = { - /** The ID of the error instance */ - id?: string; - /** A description of what went wrong. */ - message?: string; - /** The name of the error kind */ - name?: string; -}; diff --git a/frontend/src/openapi/models/index.ts b/frontend/src/openapi/models/index.ts index 250bddce97..175c175e67 100644 --- a/frontend/src/openapi/models/index.ts +++ b/frontend/src/openapi/models/index.ts @@ -124,9 +124,6 @@ export * from './archiveProject401'; export * from './archiveProject403'; export * from './archiveReleasePlanTemplate401'; export * from './archiveReleasePlanTemplate403'; -export * from './archivedFeatureSchema'; -export * from './archivedFeatureSchemaEnvironmentsItem'; -export * from './archivedFeaturesSchema'; export * from './bannerSchema'; export * from './bannersSchema'; export * from './batchFeaturesSchema'; @@ -710,10 +707,6 @@ export * from './getApplication404'; export * from './getApplicationEnvironmentInstances404'; export * from './getApplicationOverview404'; export * from './getApplicationsParams'; -export * from './getArchivedFeatures401'; -export * from './getArchivedFeatures403'; -export * from './getArchivedFeaturesByProjectId401'; -export * from './getArchivedFeaturesByProjectId403'; export * from './getBanners401'; export * from './getBaseUsersAndGroups401'; export * from './getChangeRequest404'; 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 9c771b4087..59fec59890 100644 --- a/src/lib/features/feature-search/feature.search.e2e.test.ts +++ b/src/lib/features/feature-search/feature.search.e2e.test.ts @@ -223,7 +223,9 @@ const searchFeaturesWithoutQueryParams = async (expectedCode = 200) => { }; const getProjectArchive = async (projectId = 'default', expectedCode = 200) => { return app.request - .get(`/api/admin/archive/features/${projectId}`) + .get( + `/api/admin/search/features?project=IS%3A${projectId}&archived=IS%3Atrue`, + ) .expect(expectedCode); }; diff --git a/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts b/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts index 4e70d8304d..4e1ca7f3dc 100644 --- a/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts +++ b/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts @@ -1,23 +1,17 @@ -import type { Request, Response } from 'express'; +import type { Response } from 'express'; import type { IUnleashConfig } from '../../types/option'; import type { IUnleashServices } from '../../types'; import Controller from '../../routes/controller'; import { extractUsername } from '../../util/extract-user'; -import { DELETE_FEATURE, NONE, UPDATE_FEATURE } from '../../types/permissions'; +import { DELETE_FEATURE, UPDATE_FEATURE } from '../../types/permissions'; import type FeatureToggleService from './feature-toggle-service'; import type { IAuthRequest } from '../../routes/unleash-types'; -import { serializeDates } from '../../types/serialize-dates'; import type { OpenApiService } from '../../services/openapi-service'; -import { createResponseSchema } from '../../openapi/util/create-response-schema'; import { emptyResponse, getStandardResponses, } from '../../openapi/util/standard-responses'; import type { WithTransactional } from '../../db/transaction'; -import { - archivedFeaturesSchema, - type ArchivedFeaturesSchema, -} from '../../openapi'; export default class ArchiveController extends Controller { private featureService: FeatureToggleService; @@ -43,28 +37,6 @@ export default class ArchiveController extends Controller { this.transactionalFeatureToggleService = transactionalFeatureToggleService; - this.route({ - method: 'get', - path: '/features/:projectId', - handler: this.getArchivedFeaturesByProjectId, - permission: NONE, - middleware: [ - openApiService.validPath({ - tags: ['Archive'], - operationId: 'getArchivedFeaturesByProjectId', - summary: 'Get archived features in project', - description: - 'Retrieves a list of archived features that belong to the provided project.', - responses: { - 200: createResponseSchema('archivedFeaturesSchema'), - ...getStandardResponses(401, 403), - }, - - deprecated: true, - }), - ], - }); - this.route({ method: 'delete', path: '/:featureName', @@ -108,21 +80,6 @@ export default class ArchiveController extends Controller { }); } - async getArchivedFeaturesByProjectId( - req: Request<{ projectId: string }, any, any, any>, - res: Response, - ): Promise { - const { projectId } = req.params; - const features = - await this.featureService.getArchivedFeaturesByProjectId(projectId); - this.openApiService.respondWithValidation( - 200, - res, - archivedFeaturesSchema.$id, - { version: 2, features: serializeDates(features) }, - ); - } - async deleteFeature( req: IAuthRequest<{ featureName: string }>, res: Response, diff --git a/src/lib/features/feature-toggle/converters/feature-toggle-row-converter.ts b/src/lib/features/feature-toggle/converters/feature-toggle-row-converter.ts index ff8feeb5a1..e3537ef4fa 100644 --- a/src/lib/features/feature-toggle/converters/feature-toggle-row-converter.ts +++ b/src/lib/features/feature-toggle/converters/feature-toggle-row-converter.ts @@ -220,30 +220,4 @@ export class FeatureToggleRowConverter { return this.formatToggles(result); }; - - buildArchivedFeatureToggleListFromRows = ( - rows: any[], - ): IFeatureToggleListItem[] => { - const result = rows.reduce((acc, row) => { - const feature: PartialDeep = - acc[row.name] ?? {}; - - feature.name = row.name; - feature.description = row.description; - feature.type = row.type; - feature.project = row.project; - feature.stale = row.stale; - feature.createdAt = row.created_at; - feature.impressionData = row.impression_data; - feature.lastSeenAt = row.last_seen_at; - feature.archivedAt = row.archived_at; - - this.addLastSeenByEnvironment(feature, row); - - acc[row.name] = feature; - return acc; - }, {}); - - return Object.values(result); - }; } diff --git a/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts b/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts index 63ba15d8c2..6843f6994f 100644 --- a/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts +++ b/src/lib/features/feature-toggle/fakes/fake-feature-toggle-store.ts @@ -178,10 +178,6 @@ export default class FakeFeatureToggleStore implements IFeatureToggleStore { return this.features.filter((feature) => feature.archived !== archived); } - async getArchivedFeatures(project: string): Promise { - return this.features.filter((feature) => feature.archived === true); - } - async getPlaygroundFeatures( query?: IFeatureToggleQuery, ): Promise { diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts index e8ef888d3a..43de941617 100644 --- a/src/lib/features/feature-toggle/feature-toggle-service.ts +++ b/src/lib/features/feature-toggle/feature-toggle-service.ts @@ -2162,12 +2162,6 @@ class FeatureToggleService { ); } - async getArchivedFeaturesByProjectId( - project: string, - ): Promise { - return this.featureToggleStore.getArchivedFeatures(project); - } - async getProjectId(name: string): Promise { return this.featureToggleStore.getProjectId(name); } diff --git a/src/lib/features/feature-toggle/feature-toggle-store.ts b/src/lib/features/feature-toggle/feature-toggle-store.ts index 57ffbc3c28..e7a8f0460b 100644 --- a/src/lib/features/feature-toggle/feature-toggle-store.ts +++ b/src/lib/features/feature-toggle/feature-toggle-store.ts @@ -258,32 +258,6 @@ export default class FeatureToggleStore implements IFeatureToggleStore { return rows.map(this.rowToFeature); } - async getArchivedFeatures(project: string): Promise { - const builder = new FeatureToggleListBuilder(this.db, [ - ...commonSelectColumns, - 'features.archived_at as archived_at', - ]); - - const archived = true; - builder.query('features').withLastSeenByEnvironment(archived); - - builder.addSelectColumn( - 'last_seen_at_metrics.last_seen_at as env_last_seen_at', - ); - builder.addSelectColumn( - 'last_seen_at_metrics.environment as last_seen_at_env', - ); - - const rows = await builder.internalQuery - .select(builder.getSelectColumns()) - .where({ project }) - .whereNotNull('archived_at'); - - return this.featureToggleRowConverter.buildArchivedFeatureToggleListFromRows( - rows, - ); - } - async getFeatureTypeCounts({ projectId, archived, diff --git a/src/lib/features/feature-toggle/tests/archive-feature-toggles.e2e.test.ts b/src/lib/features/feature-toggle/tests/archive-feature-toggles.e2e.test.ts index 5605157d30..8f838d978b 100644 --- a/src/lib/features/feature-toggle/tests/archive-feature-toggles.e2e.test.ts +++ b/src/lib/features/feature-toggle/tests/archive-feature-toggles.e2e.test.ts @@ -54,37 +54,42 @@ test('Should be allowed to reuse deleted toggle name', async () => { test('Should get archived toggles via project', async () => { await db.stores.featureToggleStore.deleteAll(); + const proj1 = 'proj-1'; + const proj2 = 'proj-2'; + await db.stores.projectStore.create({ - id: 'proj-1', - name: 'proj-1', + id: proj1, + name: proj1, description: '', mode: 'open' as const, }); await db.stores.projectStore.create({ - id: 'proj-2', - name: 'proj-2', + id: proj2, + name: proj2, description: '', mode: 'open' as const, }); - await db.stores.featureToggleStore.create('proj-1', { + await db.stores.featureToggleStore.create(proj1, { name: 'feat-proj-1', archived: true, createdByUserId: 9999, }); - await db.stores.featureToggleStore.create('proj-2', { + await db.stores.featureToggleStore.create(proj2, { name: 'feat-proj-2', archived: true, createdByUserId: 9999, }); - await db.stores.featureToggleStore.create('proj-2', { + await db.stores.featureToggleStore.create(proj2, { name: 'feat-proj-2-2', archived: true, createdByUserId: 9999, }); await app.request - .get('/api/admin/archive/features/proj-1') + .get( + `/api/admin/search/features?project=IS%3A${proj1}&archived=IS%3Atrue`, + ) .expect(200) .expect('Content-Type', /json/) .expect((res) => { @@ -92,7 +97,9 @@ test('Should get archived toggles via project', async () => { }); await app.request - .get('/api/admin/archive/features/proj-2') + .get( + `/api/admin/search/features?project=IS%3A${proj2}&archived=IS%3Atrue`, + ) .expect(200) .expect('Content-Type', /json/) .expect((res) => { diff --git a/src/lib/features/feature-toggle/tests/feature-toggle-last-seen-at.e2e.test.ts b/src/lib/features/feature-toggle/tests/feature-toggle-last-seen-at.e2e.test.ts index 58441f73e3..466eb314fe 100644 --- a/src/lib/features/feature-toggle/tests/feature-toggle-last-seen-at.e2e.test.ts +++ b/src/lib/features/feature-toggle/tests/feature-toggle-last-seen-at.e2e.test.ts @@ -80,28 +80,6 @@ test('response should include last seen at per environment for multiple environm expect(production.lastSeenAt).toEqual('2023-10-01T12:34:56.000Z'); }); -test('response should include last seen at per environment for multiple environments in /api/admin/archive/features/:projectId', async () => { - const featureName = 'multiple-environment-last-seen-at-archived-project'; - await setupLastSeenAtTest(featureName); - - await app.request - .delete(`/api/admin/projects/default/features/${featureName}`) - .expect(202); - - const { body } = await app.request.get( - `/api/admin/archive/features/default`, - ); - - const featureEnvironments = body.features[0].environments; - const [development, production] = featureEnvironments; - - expect(development.name).toBe('development'); - expect(development.lastSeenAt).toEqual('2023-10-01T12:34:56.000Z'); - - expect(production.name).toBe('production'); - expect(production.lastSeenAt).toEqual('2023-10-01T12:34:56.000Z'); -}); - test('response should include last seen at per environment correctly for a single toggle /api/admin/project/:projectId/features/:featureName', async () => { const featureName = 'multiple-environment-last-seen-at-single-toggle'; await app.createFeature(featureName); diff --git a/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts b/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts index f663dd5366..1973f38498 100644 --- a/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts +++ b/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts @@ -1047,7 +1047,9 @@ test('Should archive feature flag', async () => { await app.request.get(`${url}/${name}`).expect(404); const { body } = await app.request - .get(`/api/admin/archive/features/${projectId}`) + .get( + `/api/admin/search/features?project=IS%3A${projectId}&archived=IS%3Atrue`, + ) .expect(200); const flag = body.features.find((f) => f.name === name); diff --git a/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts b/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts index cf46f9a563..776a0627af 100644 --- a/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts +++ b/src/lib/features/feature-toggle/types/feature-toggle-store-type.ts @@ -54,8 +54,6 @@ export interface IFeatureToggleStore extends Store { archived?: boolean, ): Promise; - getArchivedFeatures(project: string): Promise; - getPlaygroundFeatures( featureQuery?: IFeatureToggleQuery, ): Promise; diff --git a/src/lib/openapi/spec/archived-feature-schema.ts b/src/lib/openapi/spec/archived-feature-schema.ts deleted file mode 100644 index 857209897e..0000000000 --- a/src/lib/openapi/spec/archived-feature-schema.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { FromSchema } from 'json-schema-to-ts'; - -export const archivedFeatureSchema = { - $id: '#/components/schemas/archivedFeatureSchema', - type: 'object', - additionalProperties: false, - required: ['name', 'project'], - description: 'An archived project feature flag definition', - properties: { - name: { - type: 'string', - example: 'disable-comments', - description: 'Unique feature name', - }, - type: { - type: 'string', - example: 'kill-switch', - description: - 'Type of the flag e.g. experiment, kill-switch, release, operational, permission', - }, - description: { - type: 'string', - nullable: true, - example: - 'Controls disabling of the comments section in case of an incident', - description: 'Detailed description of the feature', - }, - project: { - type: 'string', - example: 'dx-squad', - description: 'Name of the project the feature belongs to', - }, - stale: { - type: 'boolean', - example: false, - description: - '`true` if the feature is stale based on the age and feature type, otherwise `false`.', - }, - impressionData: { - type: 'boolean', - example: false, - description: - '`true` if the impression data collection is enabled for the feature, otherwise `false`.', - }, - createdAt: { - type: 'string', - format: 'date-time', - example: '2023-01-28T15:21:39.975Z', - description: 'The date the feature was created', - }, - archivedAt: { - type: 'string', - format: 'date-time', - example: '2023-01-29T15:21:39.975Z', - description: 'The date the feature was archived', - }, - lastSeenAt: { - type: 'string', - format: 'date-time', - nullable: true, - deprecated: true, - example: '2023-01-28T16:21:39.975Z', - description: - 'The date when metrics where last collected for the feature. This field was deprecated in v5, use the one in featureEnvironmentSchema', - }, - environments: { - type: 'array', - deprecated: true, - description: - 'The list of environments where the feature can be used', - items: { - type: 'object', - properties: { - name: { - type: 'string', - example: 'my-dev-env', - description: 'The name of the environment', - }, - lastSeenAt: { - type: 'string', - format: 'date-time', - nullable: true, - example: '2023-01-28T16:21:39.975Z', - description: - 'The date when metrics where last collected for the feature environment', - }, - enabled: { - type: 'boolean', - example: true, - description: - '`true` if the feature is enabled for the environment, otherwise `false`.', - }, - }, - }, - }, - }, - components: { - schemas: {}, - }, -} as const; - -export type ArchivedFeatureSchema = FromSchema; diff --git a/src/lib/openapi/spec/archived-features-schema.ts b/src/lib/openapi/spec/archived-features-schema.ts deleted file mode 100644 index b435b4246d..0000000000 --- a/src/lib/openapi/spec/archived-features-schema.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { FromSchema } from 'json-schema-to-ts'; -import { archivedFeatureSchema } from './archived-feature-schema'; - -export const archivedFeaturesSchema = { - $id: '#/components/schemas/archivedFeaturesSchema', - type: 'object', - additionalProperties: false, - required: ['version', 'features'], - description: 'A list of archived features', - properties: { - version: { - type: 'integer', - description: "The version of the feature's schema", - }, - features: { - type: 'array', - items: { - $ref: '#/components/schemas/archivedFeatureSchema', - }, - description: 'A list of features', - }, - }, - components: { - schemas: { - archivedFeatureSchema, - }, - }, -} as const; - -export type ArchivedFeaturesSchema = FromSchema; diff --git a/src/lib/openapi/spec/index.ts b/src/lib/openapi/spec/index.ts index 325c77ba91..976386b6ce 100644 --- a/src/lib/openapi/spec/index.ts +++ b/src/lib/openapi/spec/index.ts @@ -24,8 +24,6 @@ export * from './application-overview-schema'; export * from './application-schema'; export * from './application-usage-schema'; export * from './applications-schema'; -export * from './archived-feature-schema'; -export * from './archived-features-schema'; export * from './batch-features-schema'; export * from './batch-stale-schema'; export * from './bulk-metrics-schema'; diff --git a/src/test/e2e/api/admin/feature-archive.e2e.test.ts b/src/test/e2e/api/admin/feature-archive.e2e.test.ts index ddf417387d..f6ed90aec8 100644 --- a/src/test/e2e/api/admin/feature-archive.e2e.test.ts +++ b/src/test/e2e/api/admin/feature-archive.e2e.test.ts @@ -68,7 +68,9 @@ afterAll(async () => { test('returns three archived flags', async () => { expect.assertions(1); return app.request - .get(`/api/admin/archive/features/${DEFAULT_PROJECT}`) + .get( + `/api/admin/search/features?project=IS%3A${DEFAULT_PROJECT}&archived=IS%3Atrue`, + ) .expect('Content-Type', /json/) .expect(200) .expect((res) => { @@ -79,7 +81,9 @@ test('returns three archived flags', async () => { test('returns three archived flags with archivedAt', async () => { expect.assertions(2); return app.request - .get(`/api/admin/archive/features/${DEFAULT_PROJECT}`) + .get( + `/api/admin/search/features?project=IS%3A${DEFAULT_PROJECT}&archived=IS%3Atrue`, + ) .expect('Content-Type', /json/) .expect(200) .expect((res) => { @@ -238,7 +242,9 @@ test('Should be able to bulk archive features', async () => { .expect(202); const { body } = await app.request - .get(`/api/admin/archive/features/${DEFAULT_PROJECT}`) + .get( + `/api/admin/search/features?project=IS%3A${DEFAULT_PROJECT}&archived=IS%3Atrue`, + ) .expect(200); const archivedFeatures = body.features.filter(