From 9ea66e8850d768352d1171e5731cbe705a024f2d Mon Sep 17 00:00:00 2001 From: David Leek Date: Mon, 27 May 2024 09:24:09 +0200 Subject: [PATCH] chore: remove deprecated legacy features endpoint (#7129) This PR is part of #4380 - Remove legacy `/api/feature` endpoint. ## About the changes ### Frontend - Removes the useFeatures hook - Removes the part of StrategyView that displays features using this strategy (not been working since v4.4) - Removes 2 unused features entries from routes ### Backend - Removes the /api/admin/features endpoint - Moves a couple of non-feature related tests (auth etc) to use /admin/projects endpoint instead - Removes a test that was directly related to the removed endpoint - Moves a couple of tests to the projects/features endpoint - Reworks some tests to fetch features from projects features endpoint and strategies from project strategies --- .../RedirectFeatureView.tsx | 33 --------- .../__snapshots__/routes.test.tsx.snap | 16 ----- frontend/src/component/menu/routes.ts | 17 ----- .../StrategyDetails/StrategyDetails.tsx | 18 +---- .../strategies/StrategyView/StrategyView.tsx | 13 ---- .../api/getters/useFeatures/useFeatures.ts | 27 -------- .../feature-toggle/feature-toggle-service.ts | 39 ----------- .../feature-toggle-legacy-controller.ts | 43 ------------ .../feature-toggle-last-seen-at.e2e.test.ts | 4 +- .../tests/feature-toggles.e2e.test.ts | 27 -------- .../frontend-api/frontend-api.e2e.test.ts | 2 +- .../segment/admin-segment.e2e.test.ts | 69 ++++++++++++------- .../segment/client-segment.e2e.test.ts | 31 +++++++-- src/test/e2e/api/admin/favorites.e2e.test.ts | 2 +- .../e2e/api/admin/feature.auth.e2e.test.ts | 2 +- .../api/admin/feature.custom-auth.e2e.test.ts | 2 +- .../reset-password-controller.e2e.test.ts | 8 +-- src/test/e2e/custom-auth.test.ts | 4 +- 18 files changed, 85 insertions(+), 272 deletions(-) delete mode 100644 frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx delete mode 100644 frontend/src/hooks/api/getters/useFeatures/useFeatures.ts diff --git a/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx b/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx deleted file mode 100644 index 2cbe1f2688..0000000000 --- a/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Navigate } from 'react-router-dom'; -import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures'; -import { getTogglePath } from 'utils/routePathHelpers'; -import type { FeatureSchema } from 'openapi'; -import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; - -const RedirectFeatureView = () => { - const featureId = useRequiredPathParam('featureId'); - const { features = [] } = useFeatures(); - const [featureToggle, setFeatureToggle] = useState(); - - useEffect(() => { - const toggle = features.find( - (toggle: FeatureSchema) => toggle.name === featureId, - ); - - setFeatureToggle(toggle); - }, [features, featureId]); - - if (!featureToggle?.project) { - return null; - } - - return ( - - ); -}; - -export default RedirectFeatureView; 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 43e036214f..4833d6033b 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -73,14 +73,6 @@ exports[`returns all baseRoutes 1`] = ` "title": "Create feature flag", "type": "protected", }, - { - "component": [Function], - "menu": {}, - "parent": "/features", - "path": "/projects/:projectId/features2/:featureId", - "title": ":featureId", - "type": "protected", - }, { "component": { "$$typeof": Symbol(react.lazy), @@ -106,14 +98,6 @@ exports[`returns all baseRoutes 1`] = ` "title": "Projects", "type": "protected", }, - { - "component": [Function], - "menu": {}, - "parent": "/features", - "path": "/features/:activeTab/:featureId", - "title": ":featureId", - "type": "protected", - }, { "component": [Function], "menu": { diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index c2adf0032c..f4b3763c1d 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -18,7 +18,6 @@ import CreateTagType from 'component/tags/CreateTagType/CreateTagType'; import CreateFeature from 'component/feature/CreateFeature/CreateFeature'; import EditFeature from 'component/feature/EditFeature/EditFeature'; import ContextList from 'component/context/ContextList/ContextList/ContextList'; -import RedirectFeatureView from 'component/feature/RedirectFeatureView/RedirectFeatureView'; import { CreateIntegration } from 'component/integrations/CreateIntegration/CreateIntegration'; import { EditIntegration } from 'component/integrations/EditIntegration/EditIntegration'; import { CopyFeatureToggle } from 'component/feature/CopyFeature/CopyFeature'; @@ -110,14 +109,6 @@ export const routes: IRoute[] = [ type: 'protected', menu: {}, }, - { - path: '/projects/:projectId/features2/:featureId', - parent: '/features', - title: ':featureId', - component: RedirectFeatureView, - type: 'protected', - menu: {}, - }, { path: '/projects/:projectId/*', parent: '/projects', @@ -136,14 +127,6 @@ export const routes: IRoute[] = [ }, // Features - { - path: '/features/:activeTab/:featureId', - parent: '/features', - title: ':featureId', - component: RedirectFeatureView, - type: 'protected', - menu: {}, - }, { path: '/search', title: 'Search', diff --git a/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx b/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx index 166f3ed856..e068491a44 100644 --- a/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx +++ b/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx @@ -12,20 +12,17 @@ import RadioButtonChecked from '@mui/icons-material/RadioButtonChecked'; import { AppsLinkList } from 'component/common'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import styles from '../../strategies.module.scss'; -import { TogglesLinkList } from 'component/strategies/TogglesLinkList/TogglesLinkList'; import type { IStrategy, IStrategyParameter } from 'interfaces/strategy'; -import type { ApplicationSchema, FeatureSchema } from 'openapi'; +import type { ApplicationSchema } from 'openapi'; interface IStrategyDetailsProps { strategy: IStrategy; applications: ApplicationSchema[]; - toggles: FeatureSchema[]; } export const StrategyDetails = ({ strategy, applications, - toggles, }: IStrategyDetailsProps) => { const theme = useTheme(); const { parameters = [] } = strategy; @@ -84,7 +81,7 @@ export const StrategyDetails = ({ {renderParameters(parameters)} - 0 ? 6 : 12}> +
Applications using this strategy{' '} {applications.length >= 1000 && '(Capped at 1000)'} @@ -92,17 +89,6 @@ export const StrategyDetails = ({
- - 0} - show={() => ( - -
Toggles using this strategy
-
- -
- )} - /> ); diff --git a/frontend/src/component/strategies/StrategyView/StrategyView.tsx b/frontend/src/component/strategies/StrategyView/StrategyView.tsx index 2bc1309e84..b783ec8bcc 100644 --- a/frontend/src/component/strategies/StrategyView/StrategyView.tsx +++ b/frontend/src/component/strategies/StrategyView/StrategyView.tsx @@ -3,7 +3,6 @@ import { useNavigate } from 'react-router-dom'; import { UPDATE_STRATEGY } from 'component/providers/AccessProvider/permissions'; import { PageContent } from 'component/common/PageContent/PageContent'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; -import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures'; import useApplications from 'hooks/api/getters/useApplications/useApplications'; import { StrategyDetails } from './StrategyDetails/StrategyDetails'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; @@ -11,24 +10,13 @@ import PermissionIconButton from 'component/common/PermissionIconButton/Permissi import Edit from '@mui/icons-material/Edit'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; -import type { FeatureSchema } from 'openapi/models'; export const StrategyView = () => { const name = useRequiredPathParam('name'); const { strategies } = useStrategies(); - const { features = [] } = useFeatures(); const { applications } = useApplications(); const navigate = useNavigate(); - // Has been broken since the migration to environments. We need to create an - // endpoint that returns all environments and strategies for all features to make this - // work properly OR alternatively create an endpoint that abstracts this logic into the backend - const toggles = features.filter((toggle: FeatureSchema) => { - return toggle?.environments - ?.flatMap((env) => env.strategies) - .some((strategy) => strategy && strategy.name === name); - }); - const strategy = strategies.find((strategy) => strategy.name === name); const handleEdit = () => { @@ -64,7 +52,6 @@ export const StrategyView = () => { diff --git a/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts b/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts deleted file mode 100644 index a57d668796..0000000000 --- a/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { FeaturesSchema } from 'openapi'; -import useSWR from 'swr'; -import { formatApiPath } from 'utils/formatPath'; -import handleErrorResponses from '../httpErrorResponseHandler'; - -const fetcher = (path: string) => { - return fetch(path) - .then(handleErrorResponses('Feature flag')) - .then((res) => res.json()); -}; - -export const useFeatures = () => { - const { data, error, mutate } = useSWR( - formatApiPath('api/admin/features'), - fetcher, - { - refreshInterval: 15 * 1000, // ms - }, - ); - - return { - features: data?.features, - loading: !error && !data, - refetchFeatures: mutate, - error, - }; -}; diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts index 88dd7e36de..ad7fb75daa 100644 --- a/src/lib/features/feature-toggle/feature-toggle-service.ts +++ b/src/lib/features/feature-toggle/feature-toggle-service.ts @@ -1099,41 +1099,6 @@ class FeatureToggleService { return features as FeatureConfigurationClient[]; } - /** - * @deprecated Legacy! - * - * Used to retrieve metadata of all feature flags defined in Unleash. - * @param query - Allow you to limit search based on criteria such as project, tags, namePrefix. See @IFeatureToggleQuery - * @param userId - Used to find / mark features as favorite based on users preferences - * @param archived - Return archived or active flags - * @returns - */ - async getFeatureToggles( - query?: IFeatureToggleQuery, - userId?: number, - archived: boolean = false, - ): Promise { - // Remove with with feature flag - const features = await this.featureToggleStore.getFeatureToggleList( - query, - userId, - archived, - ); - - if (userId) { - const projectAccess = - await this.privateProjectChecker.getUserAccessibleProjects( - userId, - ); - return projectAccess.mode === 'all' - ? features - : features.filter((f) => - projectAccess.projects.includes(f.project), - ); - } - return features; - } - async getFeatureOverview( params: IFeatureProjectUserParams, ): Promise { @@ -1949,10 +1914,6 @@ class FeatureToggleService { ); } - async getArchivedFeatures(): Promise { - return this.getFeatureToggles({}, undefined, true); - } - // TODO: add project id. async deleteFeature( featureName: string, diff --git a/src/lib/features/feature-toggle/legacy/feature-toggle-legacy-controller.ts b/src/lib/features/feature-toggle/legacy/feature-toggle-legacy-controller.ts index 7b05b27914..0c46f7c70d 100644 --- a/src/lib/features/feature-toggle/legacy/feature-toggle-legacy-controller.ts +++ b/src/lib/features/feature-toggle/legacy/feature-toggle-legacy-controller.ts @@ -10,13 +10,8 @@ import type { IFeatureToggleQuery } from '../../../types/model'; import type FeatureTagService from '../../../services/feature-tag-service'; import type { IAuthRequest } from '../../../routes/unleash-types'; import { DEFAULT_ENV } from '../../../util/constants'; -import { - featuresSchema, - type FeaturesSchema, -} from '../../../openapi/spec/features-schema'; import type { TagSchema } from '../../../openapi/spec/tag-schema'; import type { TagsSchema } from '../../../openapi/spec/tags-schema'; -import { serializeDates } from '../../../types/serialize-dates'; import type { OpenApiService } from '../../../services/openapi-service'; import { createRequestSchema } from '../../../openapi/util/create-request-schema'; import { @@ -55,27 +50,6 @@ class FeatureController extends Controller { this.openApiService = openApiService; this.service = featureToggleServiceV2; - this.route({ - method: 'get', - path: '', - handler: this.getAllToggles, - permission: NONE, - middleware: [ - openApiService.validPath({ - tags: ['Features'], - operationId: 'getAllToggles', - responses: { - 200: createResponseSchema('featuresSchema'), - ...getStandardResponses(401, 403), - }, - summary: 'Get all feature flags (deprecated)', - description: - 'Gets all feature flags with their full configuration. This endpoint is **deprecated**. You should use the project-based endpoint instead (`/api/admin/projects//features`).', - deprecated: true, - }), - ], - }); - this.route({ method: 'post', path: '/validate', @@ -210,23 +184,6 @@ class FeatureController extends Controller { return query; } - async getAllToggles( - req: IAuthRequest, - res: Response, - ): Promise { - const query = await this.prepQuery(req.query); - - const { user } = req; - const features = await this.service.getFeatureToggles(query, user.id); - - this.openApiService.respondWithValidation( - 200, - res, - featuresSchema.$id, - { version, features: serializeDates(features) }, - ); - } - async getToggle( req: Request<{ featureName: string }, any, any, any>, res: Response, 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 4c05a20c50..e4fc8e242e 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 @@ -70,7 +70,7 @@ test('should return last seen at per env for /api/admin/features', async () => { await insertLastSeenAt('lastSeenAtPerEnv', db.rawDatabase, 'default'); const response = await app.request - .get('/api/admin/features') + .get('/api/admin/projects/default/features') .expect('Content-Type', /json/) .expect(200); @@ -88,7 +88,7 @@ test('response should include last seen at per environment for multiple environm await setupLastSeenAtTest(featureName); const { body } = await app.request - .get('/api/admin/features') + .get('/api/admin/projects/default/features') .expect('Content-Type', /json/) .expect(200); 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 0a698c2bf6..ab40646391 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 @@ -3667,33 +3667,6 @@ test('should not be allowed to update with invalid strategy type name', async () ); }); -test('should return correct data structure for /api/admin/features', async () => { - await app.createFeature('refactor-features'); - - const result = await app.request.get('/api/admin/features').expect(200); - - expect(result.body.features).toBeInstanceOf(Array); - - const feature = result.body.features.find( - (features) => features.name === 'refactor-features', - ); - - expect(feature).toMatchObject({ - impressionData: false, - enabled: false, - name: 'refactor-features', - description: null, - project: 'default', - stale: false, - type: 'release', - lastSeenAt: null, - variants: [], - favorite: false, - createdAt: expect.anything(), - strategies: [], - }); -}); - test('can get evaluation metrics', async () => { await app.createFeature('metric-feature'); diff --git a/src/lib/features/frontend-api/frontend-api.e2e.test.ts b/src/lib/features/frontend-api/frontend-api.e2e.test.ts index ccb22894c9..5fac9c307c 100644 --- a/src/lib/features/frontend-api/frontend-api.e2e.test.ts +++ b/src/lib/features/frontend-api/frontend-api.e2e.test.ts @@ -239,7 +239,7 @@ test('should allow requests with an admin token', async () => { test('should not allow admin requests with a frontend token', async () => { const frontendToken = await createApiToken(ApiTokenType.FRONTEND); await app.request - .get('/api/admin/features') + .get('/api/admin/projects') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(403); diff --git a/src/lib/features/segment/admin-segment.e2e.test.ts b/src/lib/features/segment/admin-segment.e2e.test.ts index 08096246e8..8db2d61174 100644 --- a/src/lib/features/segment/admin-segment.e2e.test.ts +++ b/src/lib/features/segment/admin-segment.e2e.test.ts @@ -1,5 +1,5 @@ import { randomId } from '../../util/random-id'; -import type { IFeatureToggleClient, ISegment } from '../../types/model'; +import type { ISegment } from '../../types/model'; import { collectIds } from '../../util/collect-ids'; import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init'; import getLogger from '../../../test/fixtures/no-logger'; @@ -12,13 +12,17 @@ import { setupAppWithCustomConfig, } from '../../../test/e2e/helpers/test-helper'; import type { StrategiesUsingSegment } from './segment-service-interface'; -import type { IUser } from '../../types'; +import type { IFeatureOverview, IUser } from '../../types'; let app: IUnleashTest; let db: ITestDb; const SEGMENTS_BASE_PATH = '/api/admin/segments'; -const FEATURES_LIST_BASE_PATH = '/api/admin/features'; +const FEATURES_LIST_BASE_PATH = '/api/admin/projects/default/features'; + +const getFeatureStrategiesPath = (featureName: string) => { + return `/api/admin/projects/default/features/${featureName}/environments/default/strategies`; +}; // Recursively change all Date properties to string properties. type SerializeDatesDeep = { @@ -39,12 +43,18 @@ const fetchSegmentsByStrategy = ( .expect(200) .then((res) => res.body.segments); -const fetchFeatures = (): Promise => +const fetchFeatures = (): Promise => app.request .get(FEATURES_LIST_BASE_PATH) .expect(200) .then((res) => res.body.features); +const fetchFeatureStrategies = (featureName: string) => + app.request + .get(getFeatureStrategiesPath(featureName)) + .expect(200) + .then((res) => res.body); + const fetchSegmentStrategies = ( segmentId: number, ): Promise => @@ -288,8 +298,9 @@ test('should not delete segments used by strategies', async () => { flag.name, ); const [feature] = await fetchFeatures(); + const [strategy] = await fetchFeatureStrategies(feature.name); //@ts-ignore - await addSegmentsToStrategy([segment.id], feature.strategies[0].id); + await addSegmentsToStrategy([segment.id], strategy.id); const segments = await fetchSegments(); expect(segments.length).toEqual(1); @@ -316,8 +327,9 @@ test('should delete segments used by strategies in archived feature flags', asyn flag.name, ); const [feature] = await fetchFeatures(); + const [strategy] = await fetchFeatureStrategies(feature.name); //@ts-ignore - await addSegmentsToStrategy([segment.id], feature.strategies[0].id); + await addSegmentsToStrategy([segment.id], strategy.id); const segments = await fetchSegments(); expect(segments.length).toEqual(1); @@ -372,36 +384,40 @@ test('should list strategies by segment', async () => { const [feature1, feature2, feature3] = await fetchFeatures(); const [segment1, segment2, segment3] = await fetchSegments(); + const feature1Strategies = await fetchFeatureStrategies(feature1.name); + const feature2Strategies = await fetchFeatureStrategies(feature2.name); + const feature3Strategies = await fetchFeatureStrategies(feature3.name); + await addSegmentsToStrategy( [segment1.id, segment2.id, segment3.id], //@ts-ignore - feature1.strategies[0].id, + feature1Strategies[0].id, ); await addSegmentsToStrategy( [segment2.id, segment3.id], //@ts-ignore - feature2.strategies[0].id, + feature2Strategies[0].id, ); //@ts-ignore - await addSegmentsToStrategy([segment3.id], feature3.strategies[0].id); + await addSegmentsToStrategy([segment3.id], feature3Strategies[0].id); const segmentStrategies1 = await fetchSegmentStrategies(segment1.id); const segmentStrategies2 = await fetchSegmentStrategies(segment2.id); const segmentStrategies3 = await fetchSegmentStrategies(segment3.id); expect(collectIds(segmentStrategies1.strategies)).toEqual( - collectIds(feature1.strategies), + collectIds(feature1Strategies), ); expect(collectIds(segmentStrategies2.strategies)).toEqual( - collectIds([...feature1.strategies, ...feature2.strategies]), + collectIds([...feature1Strategies, ...feature2Strategies]), ); expect(collectIds(segmentStrategies3.strategies)).toEqual( collectIds([ - ...feature1.strategies, - ...feature2.strategies, - ...feature3.strategies, + ...feature1Strategies, + ...feature2Strategies, + ...feature3Strategies, ]), ); }); @@ -448,30 +464,34 @@ test('should list segments by strategy', async () => { const [feature1, feature2, feature3] = await fetchFeatures(); const [segment1, segment2, segment3] = await fetchSegments(); + const [feature1Strategy] = await fetchFeatureStrategies(feature1.name); + const [feature2Strategy] = await fetchFeatureStrategies(feature2.name); + const [feature3Strategy] = await fetchFeatureStrategies(feature3.name); + await addSegmentsToStrategy( [segment1.id, segment2.id, segment3.id], //@ts-ignore - feature1.strategies[0].id, + feature1Strategy.id, ); await addSegmentsToStrategy( [segment2.id, segment3.id], //@ts-ignore - feature2.strategies[0].id, + feature2Strategy.id, ); //@ts-ignore - await addSegmentsToStrategy([segment3.id], feature3.strategies[0].id); + await addSegmentsToStrategy([segment3.id], feature3Strategy.id); const strategySegments1 = await fetchSegmentsByStrategy( //@ts-ignore - feature1.strategies[0].id, + feature1Strategy.id, ); const strategySegments2 = await fetchSegmentsByStrategy( //@ts-ignore - feature2.strategies[0].id, + feature2Strategy.id, ); const strategySegments3 = await fetchSegmentsByStrategy( //@ts-ignore - feature3.strategies[0].id, + feature3Strategy.id, ); expect(collectIds(strategySegments1)).toEqual( @@ -581,8 +601,9 @@ test('Should show usage in features and projects', async () => { flag.name, ); const [feature] = await fetchFeatures(); + const [strategy] = await fetchFeatureStrategies(feature.name); //@ts-ignore - await addSegmentsToStrategy([segment.id], feature.strategies[0].id); + await addSegmentsToStrategy([segment.id], strategy.id); const segments = await fetchSegments(); expect(segments).toMatchObject([ @@ -768,8 +789,9 @@ describe('detect strategy usage in change requests', () => { ); const [feature] = await fetchFeatures(); + const [strategy] = await fetchFeatureStrategies(feature.name); - const strategyId = feature.strategies[0].id; + const strategyId = strategy.id; await db.rawDatabase.table('change_request_events').insert({ feature: flag.name, @@ -827,8 +849,9 @@ describe('detect strategy usage in change requests', () => { ); const [feature] = await fetchFeatures(); + const [strategy] = await fetchFeatureStrategies(feature.name); - const strategyId = feature.strategies[0].id; + const strategyId = strategy.id; await addSegmentsToStrategy([segment.id], strategyId!); await db.rawDatabase.table('change_request_events').insert({ diff --git a/src/lib/features/segment/client-segment.e2e.test.ts b/src/lib/features/segment/client-segment.e2e.test.ts index 091165eeca..fbf4cc9c92 100644 --- a/src/lib/features/segment/client-segment.e2e.test.ts +++ b/src/lib/features/segment/client-segment.e2e.test.ts @@ -6,6 +6,7 @@ import { } from '../../../test/e2e/helpers/test-helper'; import type { IConstraint, + IFeatureOverview, IFeatureToggleClient, ISegment, } from '../../types/model'; @@ -35,13 +36,23 @@ const fetchSegments = (): Promise => { return app.services.segmentService.getAll(); }; -const fetchFeatures = (): Promise => { +const fetchFeatures = (): Promise => { return app.request - .get(`/api/admin/features`) + .get(`/api/admin/projects/default/features`) .expect(200) .then((res) => res.body.features); }; +const getFeatureStrategiesPath = (featureName: string) => { + return `/api/admin/projects/default/features/${featureName}/environments/default/strategies`; +}; + +const fetchFeatureStrategies = (featureName: string) => + app.request + .get(getFeatureStrategiesPath(featureName)) + .expect(200) + .then((res) => res.body); + const fetchClientFeatures = (): Promise => { return app.request .get(FEATURES_CLIENT_BASE_PATH) @@ -276,8 +287,11 @@ test('should clone feature strategy segments', async () => { await createFeatureToggle(mockFeatureToggle()); const [feature1, feature2] = await fetchFeatures(); - const strategy1 = feature1.strategies[0].id; - const strategy2 = feature2.strategies[0].id; + const [feature1Strategy] = await fetchFeatureStrategies(feature1.name); + const [feature2Strategy] = await fetchFeatureStrategies(feature2.name); + + const strategy1 = feature1Strategy.id; + const strategy2 = feature2Strategy.id; let segments1 = await app.services.segmentService.getByStrategy(strategy1!); let segments2 = await app.services.segmentService.getByStrategy(strategy2!); @@ -322,9 +336,14 @@ test('should inline segment constraints into features by default', async () => { // add segment3 to all features for (const feature of [feature1, feature2, feature3]) { + const [strt] = await fetchFeatureStrategies(feature.name); const strategy = { - ...feature.strategies[0], - segments: feature.strategies[0].segments ?? [], + id: strt.id, + name: strt.name, + constraints: strt.constraints, + parameters: strt.parameters, + variants: strt.variants, + segments: strt.segments ?? [], }; await updateFeatureStrategy(feature.name, { ...strategy, diff --git a/src/test/e2e/api/admin/favorites.e2e.test.ts b/src/test/e2e/api/admin/favorites.e2e.test.ts index ee6d65b986..7b0a77ecbb 100644 --- a/src/test/e2e/api/admin/favorites.e2e.test.ts +++ b/src/test/e2e/api/admin/favorites.e2e.test.ts @@ -162,7 +162,7 @@ test('should be favorited in admin endpoint', async () => { await favoriteFeature(featureName); const { body } = await app.request - .get('/api/admin/features') + .get('/api/admin/projects/default/features') .set('Content-Type', 'application/json') .expect(200); diff --git a/src/test/e2e/api/admin/feature.auth.e2e.test.ts b/src/test/e2e/api/admin/feature.auth.e2e.test.ts index 281425dd5f..c38d443f12 100644 --- a/src/test/e2e/api/admin/feature.auth.e2e.test.ts +++ b/src/test/e2e/api/admin/feature.auth.e2e.test.ts @@ -42,6 +42,6 @@ test('creates new feature flag with createdBy', async () => { test('should require authenticated user', async () => { expect.assertions(0); const { request, destroy } = await setupAppWithAuth(db.stores); - await request.get('/api/admin/features').expect(401); + await request.get('/api/admin/projects/default/features').expect(401); await destroy(); }); diff --git a/src/test/e2e/api/admin/feature.custom-auth.e2e.test.ts b/src/test/e2e/api/admin/feature.custom-auth.e2e.test.ts index dc9fe286eb..96926c0a2b 100644 --- a/src/test/e2e/api/admin/feature.custom-auth.e2e.test.ts +++ b/src/test/e2e/api/admin/feature.custom-auth.e2e.test.ts @@ -36,7 +36,7 @@ test('should require authenticated user', async () => { ); }; const { request, destroy } = await setupAppWithCustomAuth(stores, preHook); - await request.get('/api/admin/features').expect(401); + await request.get('/api/admin/projects/default/features').expect(401); await destroy(); }); diff --git a/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts b/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts index a48d5ad36a..0e0823471a 100644 --- a/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts +++ b/src/test/e2e/api/auth/reset-password-controller.e2e.test.ts @@ -206,7 +206,7 @@ test('Calling validate endpoint with already existing session should destroy ses email: 'user@mail.com', }) .expect(200); - await request.get('/api/admin/features').expect(200); + await request.get('/api/admin/projects').expect(200); const url = await resetTokenService.createResetPasswordUrl( user.id, adminUser.username!, @@ -214,7 +214,7 @@ test('Calling validate endpoint with already existing session should destroy ses const relative = getBackendResetUrl(url); await request.get(relative).expect(200).expect('Content-Type', /json/); - await request.get('/api/admin/features').expect(401); // we no longer should have a valid session + await request.get('/api/admin/projects').expect(401); // we no longer should have a valid session await destroy(); }); @@ -240,7 +240,7 @@ test('Calling reset endpoint with already existing session should logout/destroy email: 'user@mail.com', }) .expect(200); - await request.get('/api/admin/features').expect(200); // If we login we can access features endpoint + await request.get('/api/admin/projects').expect(200); // If we login we can access projects endpoint await request .post('/auth/reset/password') .send({ @@ -248,7 +248,7 @@ test('Calling reset endpoint with already existing session should logout/destroy password, }) .expect(200); - await request.get('/api/admin/features').expect(401); // we no longer have a valid session after using the reset password endpoint + await request.get('/api/admin/projects').expect(401); // we no longer have a valid session after using the reset password endpoint await destroy(); }); diff --git a/src/test/e2e/custom-auth.test.ts b/src/test/e2e/custom-auth.test.ts index 30ea592712..dab27fd4d6 100644 --- a/src/test/e2e/custom-auth.test.ts +++ b/src/test/e2e/custom-auth.test.ts @@ -43,7 +43,7 @@ test('Using custom auth type without defining custom middleware causes default D undefined, ); await request - .get('/api/admin/features') + .get('/api/admin/projects') .expect(401) .expect((res) => { expect(res.body.error).toBe( @@ -56,6 +56,6 @@ test('Using custom auth type without defining custom middleware causes default D test('If actually configuring a custom middleware should configure the middleware', async () => { expect.assertions(0); const { request, destroy } = await setupAppWithCustomAuth(stores, preHook); - await request.get('/api/admin/features').expect(200); + await request.get('/api/admin/projects').expect(200); await destroy(); });