mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-06 00:07:44 +01:00
c979e687ca
Currently EnableEnvironmentDialog was loaded even if no feature was touched. Now it will only load, if feature toggle was selected.
256 lines
9.4 KiB
TypeScript
256 lines
9.4 KiB
TypeScript
import { ComponentProps, useCallback, useState } from 'react';
|
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
|
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
|
import useToast from 'hooks/useToast';
|
|
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
|
|
import { UpdateEnabledMessage } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
|
|
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
|
|
import {
|
|
FeatureStrategyProdGuard,
|
|
isProdGuardEnabled,
|
|
} from 'component/feature/FeatureStrategy/FeatureStrategyProdGuard/FeatureStrategyProdGuard';
|
|
import { EnableEnvironmentDialog } from './EnableEnvironmentDialog/EnableEnvironmentDialog';
|
|
import {
|
|
OnFeatureToggleSwitchArgs,
|
|
UseFeatureToggleSwitchType,
|
|
} from './FeatureToggleSwitch.types';
|
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
|
|
type Middleware = (next: () => void) => void;
|
|
|
|
const composeAndRunMiddlewares = (middlewares: Middleware[]) => {
|
|
const runMiddleware = (currentIndex: number) => {
|
|
if (currentIndex < middlewares.length) {
|
|
middlewares[currentIndex](() => runMiddleware(currentIndex + 1));
|
|
}
|
|
};
|
|
|
|
runMiddleware(0);
|
|
};
|
|
|
|
export const useFeatureToggleSwitch: UseFeatureToggleSwitchType = (
|
|
projectId: string,
|
|
) => {
|
|
const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } =
|
|
useFeatureApi();
|
|
const { setToastData, setToastApiError } = useToast();
|
|
const [prodGuardModalState, setProdGuardModalState] = useState<
|
|
ComponentProps<typeof FeatureStrategyProdGuard>
|
|
>({
|
|
open: false,
|
|
label: '',
|
|
loading: false,
|
|
onClose: () => {},
|
|
onClick: () => {},
|
|
});
|
|
const [enableEnvironmentDialogState, setEnableEnvironmentDialogState] =
|
|
useState<ComponentProps<typeof EnableEnvironmentDialog>>({
|
|
isOpen: false,
|
|
environment: '',
|
|
featureId: '',
|
|
onClose: () => {},
|
|
onActivateDisabledStrategies: () => {},
|
|
onAddDefaultStrategy: () => {},
|
|
});
|
|
const {
|
|
onChangeRequestToggle,
|
|
onChangeRequestToggleClose,
|
|
onChangeRequestToggleConfirm,
|
|
changeRequestDialogDetails,
|
|
} = useChangeRequestToggle(projectId);
|
|
const [changeRequestDialogCallback, setChangeRequestDialogCallback] =
|
|
useState<() => void>();
|
|
|
|
const onToggle = useCallback(
|
|
async (newState: boolean, config: OnFeatureToggleSwitchArgs) => {
|
|
let shouldActivateDisabledStrategies = false;
|
|
|
|
const confirmProductionChanges: Middleware = (next) => {
|
|
if (config.isChangeRequestEnabled) {
|
|
// skip if change requests are enabled
|
|
return next();
|
|
}
|
|
|
|
if (!isProdGuardEnabled(config.environmentType || '')) {
|
|
return next();
|
|
}
|
|
|
|
setProdGuardModalState({
|
|
open: true,
|
|
label: `${!newState ? 'Disable' : 'Enable'} Environment`,
|
|
loading: false,
|
|
onClose: () => {
|
|
setProdGuardModalState((prev) => ({
|
|
...prev,
|
|
open: false,
|
|
}));
|
|
config.onRollback?.();
|
|
},
|
|
onClick: () => {
|
|
setProdGuardModalState((prev) => ({
|
|
...prev,
|
|
open: false,
|
|
loading: true,
|
|
}));
|
|
next();
|
|
},
|
|
});
|
|
};
|
|
|
|
const ensureActiveStrategies: Middleware = (next) => {
|
|
if (
|
|
newState === false ||
|
|
!config.hasStrategies ||
|
|
config.hasEnabledStrategies
|
|
) {
|
|
return next();
|
|
}
|
|
|
|
setEnableEnvironmentDialogState({
|
|
isOpen: true,
|
|
environment: config.environmentName,
|
|
featureId: config.featureId,
|
|
onClose: () => {
|
|
setEnableEnvironmentDialogState((prev) => ({
|
|
...prev,
|
|
isOpen: false,
|
|
}));
|
|
config.onRollback?.();
|
|
},
|
|
onActivateDisabledStrategies: () => {
|
|
setEnableEnvironmentDialogState((prev) => ({
|
|
...prev,
|
|
isOpen: false,
|
|
}));
|
|
shouldActivateDisabledStrategies = true;
|
|
next();
|
|
},
|
|
onAddDefaultStrategy: () => {
|
|
setEnableEnvironmentDialogState((prev) => ({
|
|
...prev,
|
|
isOpen: false,
|
|
}));
|
|
next();
|
|
},
|
|
});
|
|
};
|
|
|
|
const addToChangeRequest: Middleware = (next) => {
|
|
if (!config.isChangeRequestEnabled) {
|
|
return next();
|
|
}
|
|
|
|
setChangeRequestDialogCallback(() => {
|
|
setChangeRequestDialogCallback(undefined);
|
|
// always reset to previous state when using change requests
|
|
config.onRollback?.();
|
|
});
|
|
|
|
onChangeRequestToggle(
|
|
config.featureId,
|
|
config.environmentName,
|
|
newState,
|
|
shouldActivateDisabledStrategies,
|
|
);
|
|
};
|
|
|
|
const handleToggleEnvironmentOn: Middleware = async (next) => {
|
|
if (newState !== true) {
|
|
return next();
|
|
}
|
|
|
|
try {
|
|
await toggleFeatureEnvironmentOn(
|
|
config.projectId,
|
|
config.featureId,
|
|
config.environmentName,
|
|
shouldActivateDisabledStrategies,
|
|
);
|
|
|
|
setToastData({
|
|
type: 'success',
|
|
title: `Enabled in ${config.environmentName}`,
|
|
text: `${config.featureId} is now available in ${config.environmentName} based on its defined strategies.`,
|
|
});
|
|
config.onSuccess?.();
|
|
} catch (error: unknown) {
|
|
setToastApiError(formatUnknownError(error));
|
|
config.onRollback?.();
|
|
}
|
|
};
|
|
|
|
const handleToggleEnvironmentOff: Middleware = async (next) => {
|
|
if (newState !== false) {
|
|
return next();
|
|
}
|
|
|
|
try {
|
|
await toggleFeatureEnvironmentOff(
|
|
config.projectId,
|
|
config.featureId,
|
|
config.environmentName,
|
|
);
|
|
setToastData({
|
|
type: 'success',
|
|
title: `Disabled in ${config.environmentName}`,
|
|
text: `${config.featureId} is unavailable in ${config.environmentName} and its strategies will no longer have any effect.`,
|
|
});
|
|
config.onSuccess?.();
|
|
} catch (error: unknown) {
|
|
setToastApiError(formatUnknownError(error));
|
|
config.onRollback?.();
|
|
}
|
|
};
|
|
|
|
return composeAndRunMiddlewares([
|
|
confirmProductionChanges,
|
|
ensureActiveStrategies,
|
|
addToChangeRequest,
|
|
handleToggleEnvironmentOff,
|
|
handleToggleEnvironmentOn,
|
|
]);
|
|
},
|
|
[setProdGuardModalState],
|
|
);
|
|
|
|
const featureSelected = enableEnvironmentDialogState.featureId.length !== 0;
|
|
|
|
const modals = (
|
|
<>
|
|
<FeatureStrategyProdGuard {...prodGuardModalState} />
|
|
<ConditionallyRender
|
|
condition={featureSelected}
|
|
show={
|
|
<EnableEnvironmentDialog
|
|
{...enableEnvironmentDialogState}
|
|
/>
|
|
}
|
|
/>
|
|
<ChangeRequestDialogue
|
|
isOpen={changeRequestDialogDetails.isOpen}
|
|
onClose={() => {
|
|
changeRequestDialogCallback?.();
|
|
onChangeRequestToggleClose();
|
|
}}
|
|
environment={changeRequestDialogDetails?.environment}
|
|
onConfirm={() => {
|
|
changeRequestDialogCallback?.();
|
|
onChangeRequestToggleConfirm();
|
|
}}
|
|
messageComponent={
|
|
<UpdateEnabledMessage
|
|
enabled={changeRequestDialogDetails?.enabled!}
|
|
featureName={changeRequestDialogDetails?.featureName!}
|
|
environment={changeRequestDialogDetails.environment!}
|
|
/>
|
|
}
|
|
/>
|
|
</>
|
|
);
|
|
|
|
return {
|
|
onToggle,
|
|
modals,
|
|
};
|
|
};
|