mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	refactor: get segment limits from uiConfig (#1047)
* refactor: improve useUiConfig return type * refactor: get segment limits from uiConfig
This commit is contained in:
		
							parent
							
								
									682921d5bf
								
							
						
					
					
						commit
						006b853f6c
					
				@ -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}
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ interface IDrawerMenuProps {
 | 
			
		||||
    admin?: boolean;
 | 
			
		||||
    links: Array<{
 | 
			
		||||
        value: string;
 | 
			
		||||
        icon: string | ReactNode;
 | 
			
		||||
        icon: ReactNode;
 | 
			
		||||
        href: string;
 | 
			
		||||
        title: string;
 | 
			
		||||
    }>;
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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';
 | 
			
		||||
interface IUseUIConfigOutput {
 | 
			
		||||
    uiConfig: IUiConfig;
 | 
			
		||||
    loading: boolean;
 | 
			
		||||
    error?: Error;
 | 
			
		||||
    refetch: () => void;
 | 
			
		||||
    isOss: () => boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const useUiConfig = (options: SWRConfiguration = {}) => {
 | 
			
		||||
    const fetcher = () => {
 | 
			
		||||
const useUiConfig = (): IUseUIConfigOutput => {
 | 
			
		||||
    const path = formatApiPath(`api/admin/ui-config`);
 | 
			
		||||
    const { data, error, mutate } = useSWR<IUiConfig>(path, fetcher);
 | 
			
		||||
 | 
			
		||||
        return fetch(path, {
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
            credentials: 'include',
 | 
			
		||||
        })
 | 
			
		||||
            .then(handleErrorResponses('configuration'))
 | 
			
		||||
            .then(res => res.json());
 | 
			
		||||
    };
 | 
			
		||||
    const isOss = useCallback(() => {
 | 
			
		||||
        return !data?.versionInfo?.current?.enterprise;
 | 
			
		||||
    }, [data]);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const uiConfig = useMemo(() => {
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,2 +0,0 @@
 | 
			
		||||
export const SEGMENT_VALUES_LIMIT = 100;
 | 
			
		||||
export const STRATEGY_SEGMENTS_LIMIT = 5;
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user