mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
feat: request change - add strategy (#2330)
* feat: request change - add strategy * refactor: use change request is-enabled hook
This commit is contained in:
parent
c1e0bd83b0
commit
d2000f2848
@ -8,7 +8,7 @@ import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFe
|
|||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy';
|
||||||
import {
|
import {
|
||||||
featureStrategyDocsLink,
|
featureStrategyDocsLink,
|
||||||
featureStrategyHelp,
|
featureStrategyHelp,
|
||||||
@ -27,6 +27,8 @@ import { useCollaborateData } from 'hooks/useCollaborateData';
|
|||||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||||
import { IFeatureToggle } from 'interfaces/featureToggle';
|
import { IFeatureToggle } from 'interfaces/featureToggle';
|
||||||
import { comparisonModerator } from '../featureStrategy.utils';
|
import { comparisonModerator } from '../featureStrategy.utils';
|
||||||
|
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||||
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||||
|
|
||||||
export const FeatureStrategyCreate = () => {
|
export const FeatureStrategyCreate = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
@ -39,6 +41,7 @@ export const FeatureStrategyCreate = () => {
|
|||||||
const errors = useFormErrors();
|
const errors = useFormErrors();
|
||||||
|
|
||||||
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
|
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
|
||||||
|
const { addChangeRequest } = useChangeRequestApi();
|
||||||
const { setStrategySegments } = useSegmentsApi();
|
const { setStrategySegments } = useSegmentsApi();
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
@ -47,6 +50,10 @@ export const FeatureStrategyCreate = () => {
|
|||||||
|
|
||||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
||||||
const ref = useRef<IFeatureToggle>(feature);
|
const ref = useRef<IFeatureToggle>(feature);
|
||||||
|
const isChangeRequestEnabled = useChangeRequestsEnabled();
|
||||||
|
|
||||||
|
const isChangeRequest =
|
||||||
|
isChangeRequestEnabled && environmentId === 'production'; // FIXME: get from API - is it enabled
|
||||||
|
|
||||||
const { data, staleDataNotification, forceRefreshCache } =
|
const { data, staleDataNotification, forceRefreshCache } =
|
||||||
useCollaborateData<IFeatureToggle>(
|
useCollaborateData<IFeatureToggle>(
|
||||||
@ -77,27 +84,53 @@ export const FeatureStrategyCreate = () => {
|
|||||||
}
|
}
|
||||||
}, [featureId, strategyDefinition]);
|
}, [featureId, strategyDefinition]);
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onAddStrategy = async (payload: IFeatureStrategyPayload) => {
|
||||||
try {
|
const created = await addStrategyToFeature(
|
||||||
const created = await addStrategyToFeature(
|
projectId,
|
||||||
projectId,
|
featureId,
|
||||||
featureId,
|
environmentId,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
if (uiConfig.flags.SE) {
|
||||||
|
await setStrategySegments({
|
||||||
environmentId,
|
environmentId,
|
||||||
createStrategyPayload(strategy)
|
projectId,
|
||||||
);
|
strategyId: created.id,
|
||||||
if (uiConfig.flags.SE) {
|
segmentIds: segments.map(s => s.id),
|
||||||
await setStrategySegments({
|
|
||||||
environmentId,
|
|
||||||
projectId,
|
|
||||||
strategyId: created.id,
|
|
||||||
segmentIds: segments.map(s => s.id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setToastData({
|
|
||||||
title: 'Strategy created',
|
|
||||||
type: 'success',
|
|
||||||
confetti: true,
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
setToastData({
|
||||||
|
title: 'Strategy created',
|
||||||
|
type: 'success',
|
||||||
|
confetti: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddStrategySuggestion = async (
|
||||||
|
payload: IFeatureStrategyPayload
|
||||||
|
) => {
|
||||||
|
await addChangeRequest(projectId, environmentId, {
|
||||||
|
action: 'addStrategy',
|
||||||
|
feature: featureId,
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
// TODO: segments in change requests
|
||||||
|
setToastData({
|
||||||
|
title: 'Strategy added to draft',
|
||||||
|
type: 'success',
|
||||||
|
confetti: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
const payload = createStrategyPayload(strategy);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isChangeRequest) {
|
||||||
|
await onAddStrategySuggestion(payload);
|
||||||
|
} else {
|
||||||
|
await onAddStrategy(payload);
|
||||||
|
}
|
||||||
refetchFeature();
|
refetchFeature();
|
||||||
navigate(formatFeaturePath(projectId, featureId));
|
navigate(formatFeaturePath(projectId, featureId));
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
@ -135,6 +168,7 @@ export const FeatureStrategyCreate = () => {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
permission={CREATE_FEATURE_STRATEGY}
|
permission={CREATE_FEATURE_STRATEGY}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
|
isChangeRequest={isChangeRequest}
|
||||||
/>
|
/>
|
||||||
{staleDataNotification}
|
{staleDataNotification}
|
||||||
</FormTemplate>
|
</FormTemplate>
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
import { VFC } from 'react';
|
||||||
|
import { Alert } from '@mui/material';
|
||||||
|
|
||||||
|
interface IFeatureStrategyChangeRequestAlertProps {
|
||||||
|
environment?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FeatureStrategyChangeRequestAlert: VFC<
|
||||||
|
IFeatureStrategyChangeRequestAlertProps
|
||||||
|
> = ({ environment }) => (
|
||||||
|
<Alert severity="info">
|
||||||
|
Change requests are enabled{environment ? ` for ${environment}` : ''}.
|
||||||
|
Your changes needs to be approved before they will be live. All the
|
||||||
|
changes you do now will be added into a draft that you can submit for
|
||||||
|
review.
|
||||||
|
</Alert>
|
||||||
|
);
|
@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { Alert } from '@mui/material';
|
import { Alert } from '@mui/material';
|
||||||
import { IFeatureToggle } from 'interfaces/featureToggle';
|
import { IFeatureToggle } from 'interfaces/featureToggle';
|
||||||
import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit';
|
import { formatFeaturePath } from '../../FeatureStrategyEdit/FeatureStrategyEdit';
|
||||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||||
|
|
||||||
interface IFeatureStrategyEnabledProps {
|
interface IFeatureStrategyEnabledProps {
|
@ -5,7 +5,7 @@ import {
|
|||||||
IStrategyParameter,
|
IStrategyParameter,
|
||||||
} from 'interfaces/strategy';
|
} from 'interfaces/strategy';
|
||||||
import { FeatureStrategyType } from '../FeatureStrategyType/FeatureStrategyType';
|
import { FeatureStrategyType } from '../FeatureStrategyType/FeatureStrategyType';
|
||||||
import { FeatureStrategyEnabled } from '../FeatureStrategyEnabled/FeatureStrategyEnabled';
|
import { FeatureStrategyEnabled } from './FeatureStrategyEnabled/FeatureStrategyEnabled';
|
||||||
import { FeatureStrategyConstraints } from '../FeatureStrategyConstraints/FeatureStrategyConstraints';
|
import { FeatureStrategyConstraints } from '../FeatureStrategyConstraints/FeatureStrategyConstraints';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import {
|
import {
|
||||||
@ -27,6 +27,7 @@ import { ISegment } from 'interfaces/segment';
|
|||||||
import { IFormErrors } from 'hooks/useFormErrors';
|
import { IFormErrors } from 'hooks/useFormErrors';
|
||||||
import { validateParameterValue } from 'utils/validateParameterValue';
|
import { validateParameterValue } from 'utils/validateParameterValue';
|
||||||
import { useStrategy } from 'hooks/api/getters/useStrategy/useStrategy';
|
import { useStrategy } from 'hooks/api/getters/useStrategy/useStrategy';
|
||||||
|
import { FeatureStrategyChangeRequestAlert } from './FeatureStrategyChangeRequestAlert/FeatureStrategyChangeRequestAlert';
|
||||||
|
|
||||||
interface IFeatureStrategyFormProps {
|
interface IFeatureStrategyFormProps {
|
||||||
feature: IFeatureToggle;
|
feature: IFeatureToggle;
|
||||||
@ -34,6 +35,7 @@ interface IFeatureStrategyFormProps {
|
|||||||
permission: string;
|
permission: string;
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
isChangeRequest?: boolean;
|
||||||
strategy: Partial<IFeatureStrategy>;
|
strategy: Partial<IFeatureStrategy>;
|
||||||
setStrategy: React.Dispatch<
|
setStrategy: React.Dispatch<
|
||||||
React.SetStateAction<Partial<IFeatureStrategy>>
|
React.SetStateAction<Partial<IFeatureStrategy>>
|
||||||
@ -54,6 +56,7 @@ export const FeatureStrategyForm = ({
|
|||||||
segments,
|
segments,
|
||||||
setSegments,
|
setSegments,
|
||||||
errors,
|
errors,
|
||||||
|
isChangeRequest,
|
||||||
}: IFeatureStrategyFormProps) => {
|
}: IFeatureStrategyFormProps) => {
|
||||||
const { classes: styles } = useStyles();
|
const { classes: styles } = useStyles();
|
||||||
const [showProdGuard, setShowProdGuard] = useState(false);
|
const [showProdGuard, setShowProdGuard] = useState(false);
|
||||||
@ -115,7 +118,9 @@ export const FeatureStrategyForm = ({
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!validateAllParameters()) {
|
if (!validateAllParameters()) {
|
||||||
return;
|
return;
|
||||||
} else if (enableProdGuard) {
|
}
|
||||||
|
|
||||||
|
if (enableProdGuard && !isChangeRequest) {
|
||||||
setShowProdGuard(true);
|
setShowProdGuard(true);
|
||||||
} else {
|
} else {
|
||||||
onSubmit();
|
onSubmit();
|
||||||
@ -125,10 +130,20 @@ export const FeatureStrategyForm = ({
|
|||||||
return (
|
return (
|
||||||
<form className={styles.form} onSubmit={onSubmitWithValidation}>
|
<form className={styles.form} onSubmit={onSubmitWithValidation}>
|
||||||
<div>
|
<div>
|
||||||
<FeatureStrategyEnabled
|
<ConditionallyRender
|
||||||
projectId={feature.project}
|
condition={Boolean(isChangeRequest)}
|
||||||
featureId={feature.name}
|
show={
|
||||||
environmentId={environmentId}
|
<FeatureStrategyChangeRequestAlert
|
||||||
|
environment={environmentId}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<FeatureStrategyEnabled
|
||||||
|
projectId={feature.project}
|
||||||
|
featureId={feature.name}
|
||||||
|
environmentId={environmentId}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<hr className={styles.hr} />
|
<hr className={styles.hr} />
|
||||||
@ -176,7 +191,7 @@ export const FeatureStrategyForm = ({
|
|||||||
}
|
}
|
||||||
data-testid={STRATEGY_FORM_SUBMIT_ID}
|
data-testid={STRATEGY_FORM_SUBMIT_ID}
|
||||||
>
|
>
|
||||||
Save strategy
|
{isChangeRequest ? 'Add change to draft' : 'Save strategy'}
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
Loading…
Reference in New Issue
Block a user