mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-03 01:18:43 +02:00
feat: Query complexity validation (#4017)
This commit is contained in:
parent
3acb116ab2
commit
a0862cfc10
@ -1,6 +1,6 @@
|
||||
import { FormEventHandler, useEffect, useState, VFC } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Box, Paper, useTheme } from '@mui/material';
|
||||
import { Box, Paper, useTheme, styled, Alert } from '@mui/material';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import useToast from 'hooks/useToast';
|
||||
@ -21,6 +21,11 @@ import Loader from '../../common/Loader/Loader';
|
||||
import { AdvancedPlaygroundResultsTable } from './AdvancedPlaygroundResultsTable/AdvancedPlaygroundResultsTable';
|
||||
import { AdvancedPlaygroundResponseSchema } from 'openapi';
|
||||
import { createLocalStorage } from 'utils/createLocalStorage';
|
||||
import { BadRequestError } from '../../../utils/apiUtils';
|
||||
|
||||
const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||
marginBottom: theme.spacing(3),
|
||||
}));
|
||||
|
||||
export const AdvancedPlayground: VFC<{
|
||||
FormComponent?: typeof PlaygroundForm;
|
||||
@ -39,6 +44,7 @@ export const AdvancedPlayground: VFC<{
|
||||
const theme = useTheme();
|
||||
const matches = true;
|
||||
|
||||
const [configurationError, setConfigurationError] = useState<string>();
|
||||
const [environments, setEnvironments] = useState<string[]>(
|
||||
value.environments
|
||||
);
|
||||
@ -137,12 +143,24 @@ export const AdvancedPlayground: VFC<{
|
||||
if (action && typeof action === 'function') {
|
||||
action();
|
||||
}
|
||||
setConfigurationError(undefined);
|
||||
setResults(response);
|
||||
} catch (error: unknown) {
|
||||
setToastData({
|
||||
type: 'error',
|
||||
title: `Error parsing context: ${formatUnknownError(error)}`,
|
||||
});
|
||||
if (error instanceof BadRequestError) {
|
||||
setConfigurationError(error.message);
|
||||
} else if (error instanceof SyntaxError) {
|
||||
setToastData({
|
||||
type: 'error',
|
||||
title: `Error parsing context: ${formatUnknownError(
|
||||
error
|
||||
)}`,
|
||||
});
|
||||
} else {
|
||||
setToastData({
|
||||
type: 'error',
|
||||
title: formatUnknownError(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -242,6 +260,14 @@ export const AdvancedPlayground: VFC<{
|
||||
padding: theme.spacing(4, 2),
|
||||
})}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(configurationError)}
|
||||
show={
|
||||
<StyledAlert severity="warning">
|
||||
{configurationError}
|
||||
</StyledAlert>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={loading}
|
||||
show={<Loader />}
|
||||
|
@ -15,6 +15,7 @@ import groupBy from 'lodash.groupby';
|
||||
import { omitKeys } from '../../util';
|
||||
import { AdvancedPlaygroundFeatureSchema } from '../../openapi/spec/advanced-playground-feature-schema';
|
||||
import { AdvancedPlaygroundEnvironmentFeatureSchema } from '../../openapi/spec/advanced-playground-environment-feature-schema';
|
||||
import { validateQueryComplexity } from './validateQueryComplexity';
|
||||
|
||||
type EvaluationInput = {
|
||||
features: FeatureConfigurationClient[];
|
||||
@ -54,6 +55,12 @@ export class PlaygroundService {
|
||||
);
|
||||
const contexts = generateObjectCombinations(context);
|
||||
|
||||
validateQueryComplexity(
|
||||
environments.length,
|
||||
environmentFeatures[0]?.features.length ?? 0,
|
||||
contexts.length,
|
||||
);
|
||||
|
||||
const results = await Promise.all(
|
||||
environmentFeatures.flatMap(
|
||||
({ features, featureProject, environment }) =>
|
||||
|
39
src/lib/features/playground/validateQueryComplexity.test.ts
Normal file
39
src/lib/features/playground/validateQueryComplexity.test.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { validateQueryComplexity } from './validateQueryComplexity';
|
||||
import { BadDataError } from '../../error';
|
||||
|
||||
test('should not throw error when total combinations are under MAX_COMPLEXITY', () => {
|
||||
const environmentsCount = 10;
|
||||
const featuresCount = 10;
|
||||
const contextCombinationsCount = 10;
|
||||
|
||||
expect(() =>
|
||||
validateQueryComplexity(
|
||||
environmentsCount,
|
||||
featuresCount,
|
||||
contextCombinationsCount,
|
||||
),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
test('should throw BadDataError when total combinations are over MAX_COMPLEXITY', () => {
|
||||
const environmentsCount = 2;
|
||||
const featuresCount = 200;
|
||||
const contextCombinationsCount = 10000;
|
||||
|
||||
const expectedMessage = `Rejecting evaluation as it would generate 4000000 combinations exceeding 30000 limit. Please reduce the number of selected environments (2), features (200), context field combinations (10000).`;
|
||||
|
||||
expect(() =>
|
||||
validateQueryComplexity(
|
||||
environmentsCount,
|
||||
featuresCount,
|
||||
contextCombinationsCount,
|
||||
),
|
||||
).toThrow(BadDataError);
|
||||
expect(() =>
|
||||
validateQueryComplexity(
|
||||
environmentsCount,
|
||||
featuresCount,
|
||||
contextCombinationsCount,
|
||||
),
|
||||
).toThrow(expectedMessage);
|
||||
});
|
19
src/lib/features/playground/validateQueryComplexity.ts
Normal file
19
src/lib/features/playground/validateQueryComplexity.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { BadDataError } from '../../error';
|
||||
|
||||
const MAX_COMPLEXITY = 30000;
|
||||
|
||||
export const validateQueryComplexity = (
|
||||
environmentsCount: number,
|
||||
featuresCount: number,
|
||||
contextCombinationsCount: number,
|
||||
): void => {
|
||||
const totalCount =
|
||||
environmentsCount * featuresCount * contextCombinationsCount;
|
||||
|
||||
const reason = `Rejecting evaluation as it would generate ${totalCount} combinations exceeding ${MAX_COMPLEXITY} limit. `;
|
||||
const action = `Please reduce the number of selected environments (${environmentsCount}), features (${featuresCount}), context field combinations (${contextCombinationsCount}).`;
|
||||
|
||||
if (totalCount > MAX_COMPLEXITY) {
|
||||
throw new BadDataError(reason + action);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user