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 7aac9c6d75..9dde07c560 100644
--- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
@@ -133,7 +133,14 @@ exports[`returns all baseRoutes 1`] = `
"type": "protected",
},
{
- "component": [Function],
+ "component": {
+ "$$typeof": Symbol(react.lazy),
+ "_init": [Function],
+ "_payload": {
+ "_result": [Function],
+ "_status": -1,
+ },
+ },
"hidden": false,
"menu": {
"mobile": true,
diff --git a/frontend/src/component/playground/Playground/LazyPlayground.tsx b/frontend/src/component/playground/Playground/LazyPlayground.tsx
index c27baed30b..9688f1f69e 100644
--- a/frontend/src/component/playground/Playground/LazyPlayground.tsx
+++ b/frontend/src/component/playground/Playground/LazyPlayground.tsx
@@ -1,15 +1,3 @@
import { lazy } from 'react';
-import useUiConfig from '../../../hooks/api/getters/useUiConfig/useUiConfig';
-export const LazyLegacyPlayground = lazy(() => import('./Playground'));
-export const LazyAdvancedPlayground = lazy(
- () => import('./AdvancedPlayground')
-);
-
-export const LazyPlayground = () => {
- const { uiConfig } = useUiConfig();
-
- if (uiConfig.flags.advancedPlayground) return ;
-
- return ;
-};
+export const LazyPlayground = lazy(() => import('./AdvancedPlayground'));
diff --git a/frontend/src/component/playground/Playground/Playground.tsx b/frontend/src/component/playground/Playground/Playground.tsx
deleted file mode 100644
index f06352799f..0000000000
--- a/frontend/src/component/playground/Playground/Playground.tsx
+++ /dev/null
@@ -1,223 +0,0 @@
-import { FormEventHandler, useEffect, useState, VFC } from 'react';
-import { useSearchParams } from 'react-router-dom';
-import { Box, Paper, useMediaQuery, useTheme } from '@mui/material';
-import { PageContent } from 'component/common/PageContent/PageContent';
-import { PageHeader } from 'component/common/PageHeader/PageHeader';
-import useToast from 'hooks/useToast';
-import { formatUnknownError } from 'utils/formatUnknownError';
-import { PlaygroundResultsTable } from './PlaygroundResultsTable/PlaygroundResultsTable';
-import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { usePlaygroundApi } from 'hooks/api/actions/usePlayground/usePlayground';
-import { PlaygroundResponseSchema } from 'openapi';
-import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
-import { PlaygroundForm } from './PlaygroundForm/PlaygroundForm';
-import {
- resolveDefaultEnvironment,
- resolveProjects,
- resolveResultsWidth,
-} from './playground.utils';
-import { PlaygroundGuidance } from './PlaygroundGuidance/PlaygroundGuidance';
-import { PlaygroundGuidancePopper } from './PlaygroundGuidancePopper/PlaygroundGuidancePopper';
-import Loader from '../../common/Loader/Loader';
-
-export const Playground: VFC<{}> = () => {
- const { environments: availableEnvironments } = useEnvironments();
- const theme = useTheme();
- const matches = useMediaQuery(theme.breakpoints.down('lg'));
-
- const [environments, setEnvironments] = useState([]);
- const [projects, setProjects] = useState([]);
- const [context, setContext] = useState();
- const [results, setResults] = useState<
- PlaygroundResponseSchema | undefined
- >();
- const { setToastData } = useToast();
- const [searchParams, setSearchParams] = useSearchParams();
- const { evaluatePlayground, loading } = usePlaygroundApi();
-
- useEffect(() => {
- setEnvironments([resolveDefaultEnvironment(availableEnvironments)]);
- }, [JSON.stringify(availableEnvironments)]);
-
- useEffect(() => {
- // Load initial values from URL
- try {
- const environmentFromUrl = searchParams.get('environment');
- if (environmentFromUrl) {
- setEnvironments([environmentFromUrl]);
- }
-
- let projectsArray: string[];
- let projectsFromUrl = searchParams.get('projects');
- if (projectsFromUrl) {
- projectsArray = projectsFromUrl.split(',');
- setProjects(projectsArray);
- }
-
- let contextFromUrl = searchParams.get('context');
- if (contextFromUrl) {
- contextFromUrl = decodeURI(contextFromUrl);
- setContext(contextFromUrl);
- }
-
- const makePlaygroundRequest = async () => {
- if (environmentFromUrl && contextFromUrl) {
- await evaluatePlaygroundContext(
- environmentFromUrl,
- projectsArray || '*',
- contextFromUrl
- );
- }
- };
-
- makePlaygroundRequest();
- } catch (error) {
- setToastData({
- type: 'error',
- title: `Failed to parse URL parameters: ${formatUnknownError(
- error
- )}`,
- });
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- const evaluatePlaygroundContext = async (
- environment: string,
- projects: string[] | string,
- context: string | undefined,
- action?: () => void
- ) => {
- try {
- const parsedContext = JSON.parse(context || '{}');
- const response = await evaluatePlayground({
- environment,
- projects: resolveProjects(projects),
- context: {
- appName: 'playground',
- ...parsedContext,
- },
- });
-
- if (action && typeof action === 'function') {
- action();
- }
- setResults(response);
- } catch (error: unknown) {
- setToastData({
- type: 'error',
- title: `Error parsing context: ${formatUnknownError(error)}`,
- });
- }
- };
-
- const onSubmit: FormEventHandler = async event => {
- event.preventDefault();
-
- await evaluatePlaygroundContext(
- environments[0],
- projects,
- context,
- setURLParameters
- );
- };
-
- const setURLParameters = () => {
- searchParams.set('context', encodeURI(context || '')); // always set because of native validation
- searchParams.set('environment', environments[0]);
- if (
- Array.isArray(projects) &&
- projects.length > 0 &&
- !(projects.length === 1 && projects[0] === '*')
- ) {
- searchParams.set('projects', projects.join(','));
- } else {
- searchParams.delete('projects');
- }
- setSearchParams(searchParams);
- };
-
- const formWidth = results && !matches ? '35%' : 'auto';
- const resultsWidth = resolveResultsWidth(matches, results);
-
- return (
- }
- />
- }
- disableLoading
- bodyClass={'no-padding'}
- >
-
-
-
-
-
-
- ({
- width: resultsWidth,
- transition: 'width 0.4s ease',
- padding: theme.spacing(4, 2),
- })}
- >
- }
- elseShow={
-
- }
- elseShow={}
- />
- }
- />
-
-
-
- );
-};
-
-export default Playground;
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx
deleted file mode 100644
index 00e65821a7..0000000000
--- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx
+++ /dev/null
@@ -1,303 +0,0 @@
-import { useEffect, useMemo, useState } from 'react';
-import { useSearchParams } from 'react-router-dom';
-import {
- SortingRule,
- useFlexLayout,
- useGlobalFilter,
- useSortBy,
- useTable,
-} from 'react-table';
-
-import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
-import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
-import { sortTypes } from 'utils/sortTypes';
-import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
-import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { Search } from 'component/common/Search/Search';
-import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
-import { useSearch } from 'hooks/useSearch';
-import { createLocalStorage } from 'utils/createLocalStorage';
-import { FeatureStatusCell } from './FeatureStatusCell/FeatureStatusCell';
-import { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
-import { Box, Typography, useMediaQuery, useTheme } from '@mui/material';
-import useLoading from 'hooks/useLoading';
-import { VariantCell } from './VariantCell/VariantCell';
-import { FeatureResultInfoPopoverCell } from './FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell';
-import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
-
-const defaultSort: SortingRule = { id: 'name' };
-const { value, setValue } = createLocalStorage(
- 'PlaygroundResultsTable:v1',
- defaultSort
-);
-
-interface IPlaygroundResultsTableProps {
- features?: PlaygroundFeatureSchema[];
- input?: PlaygroundRequestSchema;
- loading: boolean;
-}
-
-export const PlaygroundResultsTable = ({
- features,
- input,
- loading,
-}: IPlaygroundResultsTableProps) => {
- const [searchParams, setSearchParams] = useSearchParams();
- const ref = useLoading(loading);
- const [searchValue, setSearchValue] = useState(
- searchParams.get('search') || ''
- );
- const theme = useTheme();
- const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
- const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
-
- const COLUMNS = useMemo(() => {
- return [
- {
- Header: 'Name',
- accessor: 'name',
- searchable: true,
- minWidth: 160,
- Cell: ({ value, row: { original } }: any) => (
-
- ),
- },
- {
- Header: 'Project ID',
- accessor: 'projectId',
- sortType: 'alphanumeric',
- filterName: 'projectId',
- searchable: true,
- maxWidth: 170,
- Cell: ({ value }: any) => (
-
- ),
- },
- {
- Header: 'Variant',
- id: 'variant',
- accessor: 'variant.name',
- sortType: 'alphanumeric',
- filterName: 'variant',
- searchable: true,
- maxWidth: 200,
- Cell: ({
- value,
- row: {
- original: { variant, feature, variants, isEnabled },
- },
- }: any) => (
-
- ),
- },
- {
- id: 'isEnabled',
- Header: 'isEnabled',
- filterName: 'isEnabled',
- accessor: (row: PlaygroundFeatureSchema) =>
- row?.isEnabled
- ? 'true'
- : row?.strategies?.result === 'unknown'
- ? 'unknown'
- : 'false',
- Cell: ({ row }: any) => (
-
- ),
- sortType: 'playgroundResultState',
- maxWidth: 120,
- sortInverted: true,
- },
- {
- Header: '',
- maxWidth: 70,
- id: 'info',
- Cell: ({ row }: any) => (
-
- ),
- },
- ];
- }, [input]);
-
- const {
- data: searchedData,
- getSearchText,
- getSearchContext,
- } = useSearch(COLUMNS, searchValue, features || []);
-
- const data = useMemo(() => {
- return loading
- ? Array(5).fill({
- name: 'Feature name',
- projectId: 'FeatureProject',
- variant: { name: 'FeatureVariant', variants: [] },
- enabled: true,
- })
- : searchedData;
- }, [searchedData, loading]);
-
- const [initialState] = useState(() => ({
- sortBy: [
- {
- id: searchParams.get('sort') || value.id,
- desc: searchParams.has('order')
- ? searchParams.get('order') === 'desc'
- : value.desc,
- },
- ],
- }));
-
- const {
- headerGroups,
- rows,
- state: { sortBy },
- prepareRow,
- setHiddenColumns,
- } = useTable(
- {
- initialState,
- columns: COLUMNS as any,
- data: data as any,
- sortTypes,
- autoResetGlobalFilter: false,
- autoResetHiddenColumns: false,
- autoResetSortBy: false,
- disableSortRemove: true,
- disableMultiSort: true,
- defaultColumn: {
- Cell: HighlightCell,
- },
- },
- useGlobalFilter,
- useFlexLayout,
- useSortBy
- );
-
- useConditionallyHiddenColumns(
- [
- {
- condition: isExtraSmallScreen,
- columns: ['variant'],
- },
- {
- condition: isSmallScreen,
- columns: ['projectId'],
- },
- ],
- setHiddenColumns,
- COLUMNS
- );
-
- useEffect(() => {
- if (loading) {
- return;
- }
- const tableState: Record =
- Object.fromEntries(searchParams);
- tableState.sort = sortBy[0].id;
- if (sortBy[0].desc) {
- tableState.order = 'desc';
- } else if (tableState.order) {
- delete tableState.order;
- }
- if (searchValue) {
- tableState.search = searchValue;
- } else {
- delete tableState.search;
- }
-
- setSearchParams(tableState, {
- replace: true,
- });
- setValue({ id: sortBy[0].id, desc: sortBy[0].desc || false });
-
- // eslint-disable-next-line react-hooks/exhaustive-deps -- don't re-render after search params change
- }, [loading, sortBy, searchValue]);
-
- return (
- <>
-
-
- {features !== undefined && !loading
- ? `Results (${
- rows.length < data.length
- ? `${rows.length} of ${data.length}`
- : data.length
- })`
- : 'Results'}
-
-
-
-
- (
-
- {data === undefined
- ? 'None of the feature toggles were evaluated yet.'
- : 'No results found.'}
-
- )}
- elseShow={() => (
-
-
-
-
- 0
- }
- show={
-
- No feature toggles found matching “
- {searchValue}”
-
- }
- />
-
-
- No features toggles to display
-
- }
- />
-
- )}
- />
- >
- );
-};
diff --git a/frontend/src/hooks/api/actions/usePlayground/usePlayground.ts b/frontend/src/hooks/api/actions/usePlayground/usePlayground.ts
index 241abf7104..13ebebdcf8 100644
--- a/frontend/src/hooks/api/actions/usePlayground/usePlayground.ts
+++ b/frontend/src/hooks/api/actions/usePlayground/usePlayground.ts
@@ -13,21 +13,6 @@ export const usePlaygroundApi = () => {
const URI = 'api/admin/playground';
- const evaluatePlayground = async (payload: PlaygroundRequestSchema) => {
- const req = createRequest(URI, {
- method: 'POST',
- body: JSON.stringify(payload),
- });
-
- try {
- const res = await makeRequest(req.caller, req.id);
-
- return res.json() as Promise;
- } catch (error) {
- throw error;
- }
- };
-
const evaluateAdvancedPlayground = async (
payload: AdvancedPlaygroundRequestSchema
) => {
@@ -47,7 +32,6 @@ export const usePlaygroundApi = () => {
};
return {
- evaluatePlayground,
evaluateAdvancedPlayground,
errors,
loading,
diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap
index c9b0e4652d..89a6733d66 100644
--- a/src/lib/__snapshots__/create-config.test.ts.snap
+++ b/src/lib/__snapshots__/create-config.test.ts.snap
@@ -67,7 +67,6 @@ exports[`should create default config 1`] = `
"isEnabled": [Function],
},
"flags": {
- "advancedPlayground": false,
"anonymiseEventLog": false,
"caseInsensitiveInOperators": false,
"cleanClientApi": false,
@@ -102,7 +101,6 @@ exports[`should create default config 1`] = `
},
"flagResolver": FlagResolver {
"experiments": {
- "advancedPlayground": false,
"anonymiseEventLog": false,
"caseInsensitiveInOperators": false,
"cleanClientApi": false,
diff --git a/src/lib/features/playground/playground.ts b/src/lib/features/playground/playground.ts
index a4d3d25996..d21369338b 100644
--- a/src/lib/features/playground/playground.ts
+++ b/src/lib/features/playground/playground.ts
@@ -55,7 +55,7 @@ export default class PlaygroundController extends Controller {
},
requestBody: createRequestSchema('playgroundRequestSchema'),
description:
- 'Use the provided `context`, `environment`, and `projects` to evaluate toggles on this Unleash instance. Returns a list of all toggles that match the parameters and what they evaluate to. The response also contains the input parameters that were provided.',
+ 'Deprecated. Will be removed in the next Unleash major update. Use the provided `context`, `environment`, and `projects` to evaluate toggles on this Unleash instance. Returns a list of all toggles that match the parameters and what they evaluate to. The response also contains the input parameters that were provided.',
summary:
'Evaluate an Unleash context against a set of environments and projects.',
}),
@@ -115,27 +115,23 @@ export default class PlaygroundController extends Controller {
req: Request,
res: Response,
): Promise {
- if (this.flagResolver.isEnabled('advancedPlayground')) {
- const { payload } =
- this.flagResolver.getVariant('advancedPlayground');
- const limit =
- payload?.value && Number.isInteger(parseInt(payload?.value))
- ? parseInt(payload?.value)
- : 15000;
+ // used for runtime control, do not remove
+ const { payload } = this.flagResolver.getVariant('advancedPlayground');
+ const limit =
+ payload?.value && Number.isInteger(parseInt(payload?.value))
+ ? parseInt(payload?.value)
+ : 15000;
- const result = await this.playgroundService.evaluateAdvancedQuery(
- req.body.projects || '*',
- req.body.environments,
- req.body.context,
- limit,
- );
+ const result = await this.playgroundService.evaluateAdvancedQuery(
+ req.body.projects || '*',
+ req.body.environments,
+ req.body.context,
+ limit,
+ );
- const response: AdvancedPlaygroundResponseSchema =
- advancedPlaygroundViewModel(req.body, result);
+ const response: AdvancedPlaygroundResponseSchema =
+ advancedPlaygroundViewModel(req.body, result);
- res.json(response);
- } else {
- res.status(409).end();
- }
+ res.json(response);
}
}
diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts
index e890fa34e3..3a92a314ed 100644
--- a/src/lib/types/experimental.ts
+++ b/src/lib/types/experimental.ts
@@ -95,10 +95,6 @@ const flags: IFlags = {
process.env.DISABLE_NOTIFICATIONS,
false,
),
- advancedPlayground: parseEnvVarBoolean(
- process.env.ADVANCED_PLAYGROUND,
- false,
- ),
customRootRoles: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_CUSTOM_ROOT_ROLES,
false,
diff --git a/src/server-dev.ts b/src/server-dev.ts
index 8ad182bbff..c6533fd133 100644
--- a/src/server-dev.ts
+++ b/src/server-dev.ts
@@ -37,7 +37,6 @@ process.nextTick(async () => {
embedProxyFrontend: true,
anonymiseEventLog: false,
responseTimeWithAppNameKillSwitch: false,
- advancedPlayground: true,
strategyVariant: true,
newProjectLayout: true,
emitPotentiallyStaleEvents: true,