1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-22 01:16:07 +02:00

refactor: get segment limits from uiConfig (#1047)

* refactor: improve useUiConfig return type

* refactor: get segment limits from uiConfig
This commit is contained in:
olav 2022-06-02 10:58:55 +02:00 committed by GitHub
parent 682921d5bf
commit 006b853f6c
11 changed files with 103 additions and 59 deletions

View File

@ -8,7 +8,7 @@ import {
import { FeatureStrategySegmentList } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentList';
import { useStyles } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegment.styles';
import { SegmentDocsStrategyWarning } from 'component/segments/SegmentDocs/SegmentDocs';
import { STRATEGY_SEGMENTS_LIMIT } from 'utils/segmentLimits';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
interface IFeatureStrategySegmentProps {
segments: ISegment[];
@ -19,9 +19,14 @@ export const FeatureStrategySegment = ({
segments: selectedSegments,
setSegments: setSelectedSegments,
}: IFeatureStrategySegmentProps) => {
const atSegmentsLimit = selectedSegments.length >= STRATEGY_SEGMENTS_LIMIT;
const { segments: allSegments } = useSegments();
const { classes: styles } = useStyles();
const { strategySegmentsLimit } = useSegmentLimits();
const atStrategySegmentsLimit: boolean = Boolean(
strategySegmentsLimit &&
selectedSegments.length >= strategySegmentsLimit
);
if (!allSegments || allSegments.length === 0) {
return null;
@ -48,13 +53,13 @@ export const FeatureStrategySegment = ({
return (
<>
<h3 className={styles.title}>Segmentation</h3>
{atSegmentsLimit && <SegmentDocsStrategyWarning />}
{atStrategySegmentsLimit && <SegmentDocsStrategyWarning />}
<p>Add a predefined segment to constrain this feature toggle:</p>
<AutocompleteBox
label="Select segments"
options={autocompleteOptions}
onChange={onChange}
disabled={atSegmentsLimit}
disabled={atStrategySegmentsLimit}
/>
<FeatureStrategySegmentList
segments={selectedSegments}

View File

@ -19,7 +19,7 @@ interface IDrawerMenuProps {
admin?: boolean;
links: Array<{
value: string;
icon: string | ReactNode;
icon: ReactNode;
href: string;
title: string;
}>;

View File

@ -14,8 +14,8 @@ import { SegmentForm } from '../SegmentForm/SegmentForm';
import { feedbackCESContext } from 'component/feedback/FeedbackCESContext/FeedbackCESContext';
import { segmentsDocsLink } from 'component/segments/SegmentDocs/SegmentDocs';
import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValuesCount';
import { SEGMENT_VALUES_LIMIT } from 'utils/segmentLimits';
import { SEGMENT_CREATE_BTN_ID } from 'utils/testIds';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
export const CreateSegment = () => {
const { uiConfig } = useUiConfig();
@ -38,8 +38,12 @@ export const CreateSegment = () => {
} = useSegmentForm();
const hasValidConstraints = useConstraintsValidation(constraints);
const { segmentValuesLimit } = useSegmentLimits();
const segmentValuesCount = useSegmentValuesCount(constraints);
const atSegmentValuesLimit = segmentValuesCount >= SEGMENT_VALUES_LIMIT;
const overSegmentValuesLimit: boolean = Boolean(
segmentValuesLimit && segmentValuesCount > segmentValuesLimit
);
const formatApiCode = () => {
return `curl --location --request POST '${
@ -96,7 +100,7 @@ export const CreateSegment = () => {
<CreateButton
name="segment"
permission={CREATE_SEGMENT}
disabled={!hasValidConstraints || atSegmentValuesLimit}
disabled={!hasValidConstraints || overSegmentValuesLimit}
data-testid={SEGMENT_CREATE_BTN_ID}
/>
</SegmentForm>

View File

@ -16,8 +16,8 @@ import { segmentsFormDescription } from 'component/segments/CreateSegment/Create
import { UpdateButton } from 'component/common/UpdateButton/UpdateButton';
import { segmentsDocsLink } from 'component/segments/SegmentDocs/SegmentDocs';
import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValuesCount';
import { SEGMENT_VALUES_LIMIT } from 'utils/segmentLimits';
import { SEGMENT_SAVE_BTN_ID } from 'utils/testIds';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
export const EditSegment = () => {
const segmentId = useRequiredPathParam('segmentId');
@ -46,7 +46,11 @@ export const EditSegment = () => {
const hasValidConstraints = useConstraintsValidation(constraints);
const segmentValuesCount = useSegmentValuesCount(constraints);
const atSegmentValuesLimit = segmentValuesCount >= SEGMENT_VALUES_LIMIT;
const { segmentValuesLimit } = useSegmentLimits();
const overSegmentValuesLimit: boolean = Boolean(
segmentValuesLimit && segmentValuesCount > segmentValuesLimit
);
const formatApiCode = () => {
return `curl --location --request PUT '${
@ -98,7 +102,7 @@ export const EditSegment = () => {
>
<UpdateButton
permission={UPDATE_SEGMENT}
disabled={!hasValidConstraints || atSegmentValuesLimit}
disabled={!hasValidConstraints || overSegmentValuesLimit}
data-testid={SEGMENT_SAVE_BTN_ID}
/>
</SegmentForm>

View File

@ -1,9 +1,6 @@
import { Alert } from '@mui/material';
import { useStyles } from 'component/segments/SegmentDocs/SegmentDocs.styles';
import {
STRATEGY_SEGMENTS_LIMIT,
SEGMENT_VALUES_LIMIT,
} from 'utils/segmentLimits';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
export const SegmentDocsWarning = () => {
const { classes: styles } = useStyles();
@ -25,19 +22,31 @@ export const SegmentDocsWarning = () => {
};
export const SegmentDocsValuesWarning = () => {
const { segmentValuesLimit } = useSegmentLimits();
if (typeof segmentValuesLimit === 'undefined') {
return null;
}
return (
<Alert severity="warning">
Segments is an experimental feature available to select users.
Currently, segments are limited to at most {SEGMENT_VALUES_LIMIT}{' '}
Currently, segments are limited to at most {segmentValuesLimit}{' '}
values. <SegmentLimitsLink />
</Alert>
);
};
export const SegmentDocsValuesError = (props: { values: number }) => {
const { segmentValuesLimit } = useSegmentLimits();
if (typeof segmentValuesLimit === 'undefined') {
return null;
}
return (
<Alert severity="error">
Segments are limited to at most {SEGMENT_VALUES_LIMIT} values. This
Segments are limited to at most {segmentValuesLimit} values. This
segment currently has {props.values}{' '}
{props.values === 1 ? 'value' : 'values'}.
</Alert>
@ -45,9 +54,15 @@ export const SegmentDocsValuesError = (props: { values: number }) => {
};
export const SegmentDocsStrategyWarning = () => {
const { strategySegmentsLimit } = useSegmentLimits();
if (typeof strategySegmentsLimit === 'undefined') {
return null;
}
return (
<Alert severity="warning">
Strategies are limited to {STRATEGY_SEGMENTS_LIMIT} segments.{' '}
Strategies are limited to {strategySegmentsLimit} segments.{' '}
<SegmentLimitsLink />
</Alert>
);

View File

@ -28,8 +28,8 @@ import {
SegmentDocsValuesError,
} from 'component/segments/SegmentDocs/SegmentDocs';
import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValuesCount';
import { SEGMENT_VALUES_LIMIT } from 'utils/segmentLimits';
import AccessContext from 'contexts/AccessContext';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
interface ISegmentFormPartTwoProps {
constraints: IConstraint[];
@ -52,8 +52,12 @@ export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
const { context = [] } = useUnleashContext();
const [open, setOpen] = useState(false);
const segmentValuesCount = useSegmentValuesCount(constraints);
const overSegmentValuesLimit = segmentValuesCount > SEGMENT_VALUES_LIMIT;
const modePermission = mode === 'create' ? CREATE_SEGMENT : UPDATE_SEGMENT;
const { segmentValuesLimit } = useSegmentLimits();
const overSegmentValuesLimit: boolean = Boolean(
segmentValuesLimit && segmentValuesCount > segmentValuesLimit
);
const autocompleteOptions = context.map(c => ({
value: c.name,

View File

@ -0,0 +1,16 @@
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { IUiConfig } from 'interfaces/uiConfig';
type IUseSegmentLimits = Pick<
IUiConfig,
'segmentValuesLimit' | 'strategySegmentsLimit'
>;
export const useSegmentLimits = (): IUseSegmentLimits => {
const { uiConfig } = useUiConfig();
return {
segmentValuesLimit: uiConfig.segmentValuesLimit,
strategySegmentsLimit: uiConfig.strategySegmentsLimit,
};
};

View File

@ -1,9 +1,9 @@
import { LibraryBooks } from '@mui/icons-material';
import { IUiConfig } from 'interfaces/uiConfig';
export const defaultValue = {
export const defaultValue: IUiConfig = {
name: 'Unleash',
version: '3.x',
environment: '',
slogan: 'The enterprise ready feature toggle service.',
flags: {
P: false,

View File

@ -1,40 +1,27 @@
import useSWR, { mutate, SWRConfiguration } from 'swr';
import useSWR from 'swr';
import { formatApiPath } from 'utils/formatPath';
import { defaultValue } from './defaultValue';
import { IUiConfig } from 'interfaces/uiConfig';
import handleErrorResponses from '../httpErrorResponseHandler';
import { useMemo } from 'react';
import { useMemo, useCallback } from 'react';
const REQUEST_KEY = 'api/admin/ui-config';
const useUiConfig = (options: SWRConfiguration = {}) => {
const fetcher = () => {
const path = formatApiPath(`api/admin/ui-config`);
return fetch(path, {
method: 'GET',
credentials: 'include',
})
.then(handleErrorResponses('configuration'))
.then(res => res.json());
};
const { data, error } = useSWR<IUiConfig>(REQUEST_KEY, fetcher, options);
const refetch = () => {
mutate(REQUEST_KEY);
};
const isOss = () => {
if (data?.versionInfo?.current?.enterprise) {
return false;
} else if (!data || !data.versionInfo) {
return false;
interface IUseUIConfigOutput {
uiConfig: IUiConfig;
loading: boolean;
error?: Error;
refetch: () => void;
isOss: () => boolean;
}
return true;
};
const uiConfig = useMemo(() => {
const useUiConfig = (): IUseUIConfigOutput => {
const path = formatApiPath(`api/admin/ui-config`);
const { data, error, mutate } = useSWR<IUiConfig>(path, fetcher);
const isOss = useCallback(() => {
return !data?.versionInfo?.current?.enterprise;
}, [data]);
const uiConfig: IUiConfig = useMemo(() => {
return { ...defaultValue, ...data };
}, [data]);
@ -42,9 +29,15 @@ const useUiConfig = (options: SWRConfiguration = {}) => {
uiConfig,
loading: !error && !data,
error,
refetch,
refetch: mutate,
isOss,
};
};
const fetcher = (path: string) => {
return fetch(path)
.then(handleErrorResponses('configuration'))
.then(res => res.json());
};
export default useUiConfig;

View File

@ -1,15 +1,20 @@
import { ReactNode } from 'react';
export interface IUiConfig {
authenticationType: string;
baseUriPath: string;
authenticationType?: string;
baseUriPath?: string;
flags: IFlags;
name: string;
slogan: string;
unleashUrl: string;
environment?: string;
unleashUrl?: string;
version: string;
versionInfo: IVersionInfo;
versionInfo?: IVersionInfo;
links: ILinks[];
disablePasswordAuth?: boolean;
toast?: IProclamationToast;
segmentValuesLimit?: number;
strategySegmentsLimit?: number;
}
export interface IProclamationToast {
@ -46,7 +51,7 @@ export interface IVersion {
export interface ILinks {
value: string;
icon: string;
icon: ReactNode;
href: string;
title: string;
}

View File

@ -1,2 +0,0 @@
export const SEGMENT_VALUES_LIMIT = 100;
export const STRATEGY_SEGMENTS_LIMIT = 5;