1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-10-18 20:09:08 +02:00
unleash.unleash/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx

146 lines
5.1 KiB
TypeScript
Raw Normal View History

feat: add new feature strategy create/edit pages (#739) * refactor: add param helper hooks * refactor: remove first add strategy link * refactor: add more types to useStrategies * refactor: port strategy utils to TS * refactor: replace rollout strategy icon * refactor: use a named export for useFeature * refactor: add more types to useFeature * refactor: adjust code box styles * refactor: add missing PermissionButton variant prop * refactor: add missing button icon label * refactor: move common feature components * refactor: fix StrategyConstraints error prop types * refactor: fix GeneralStrategy prop types * feat: add new feature strategy create/edit pages * refactor: remove feature strategies page * refactor: fix types in GeneralStrategy * refactor: use ConstraintAccordion on the new pages * refactor: use ConditionallyRender for remove button * refactor: rename FeatureStrategyForm component * refactor: use the Edit icon for feature strategies * refactor: fix initial edit mode for new constraints * refactor: add FeatureStrategyMenu to closed accordions * refactor: allow editing multiple constraints * refactor: show single-valued constraint value * refactor: increase feature overview strategy width * refactor: add remove button to feature overview strategies * refactor: move createEmptyConstraint to own file * refactor: disable submit button for invalid constraints * refactor: fix nested paragraphs on the metrics page * refactor: move create/edit feature strategy to modal * refactor: always open new constraints in edit mode * refactor: use a PermissionButton for the save button * refactor: remvoe unsaved constraints on cancel * refactor: clarify useConstraintsValidation logic * refactor: remove unused strategy descriptions * refactor: restore Rollout icon * refactor: remove sidebar modal slide animation * refactor: avoid constraint accordion toggle on edit/delete * refactor: truncate long strategy names * refactor: find the correct remove button
2022-03-09 14:59:24 +01:00
import React, { useEffect, useState } from 'react';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
import FormTemplate from '../../../common/FormTemplate/FormTemplate';
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useFeatureStrategyApi from '../../../../hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
import { formatUnknownError } from 'utils/format-unknown-error';
import { useHistory } from 'react-router-dom';
import useToast from '../../../../hooks/useToast';
import { IFeatureStrategy, IStrategyPayload } from 'interfaces/strategy';
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
export const FeatureStrategyEdit = () => {
const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
const environmentId = useRequiredQueryParam('environmentId');
const strategyId = useRequiredQueryParam('strategyId');
const [strategy, setStrategy] = useState<Partial<IFeatureStrategy>>({});
const { updateStrategyOnFeature, loading } = useFeatureStrategyApi();
const { feature, refetchFeature } = useFeature(projectId, featureId);
const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig();
const { unleashUrl } = uiConfig;
const { push } = useHistory();
useEffect(() => {
const savedStrategy = feature.environments
.flatMap(environment => environment.strategies)
.find(strategy => strategy.id === strategyId);
setStrategy(prev => ({ ...prev, ...savedStrategy }));
}, [strategyId, feature]);
const onSubmit = async () => {
try {
await updateStrategyOnFeature(
projectId,
featureId,
environmentId,
strategyId,
createStrategyPayload(strategy)
);
setToastData({
title: 'Strategy updated',
type: 'success',
confetti: true,
});
refetchFeature();
push(formatFeaturePath(projectId, featureId));
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
// Wait until the strategy has loaded before showing the form.
if (!strategy.id) {
return null;
}
return (
<FormTemplate
modal
title="Edit feature strategy"
description={featureStrategyHelp}
documentationLink={featureStrategyDocsLink}
formatApiCode={() =>
formatUpdateStrategyApiCode(
projectId,
featureId,
environmentId,
strategy,
unleashUrl
)
}
>
<FeatureStrategyForm
feature={feature}
strategy={strategy}
setStrategy={setStrategy}
environmentId={environmentId}
onSubmit={onSubmit}
loading={loading}
permission={UPDATE_FEATURE_STRATEGY}
/>
</FormTemplate>
);
};
export const createStrategyPayload = (
strategy: Partial<IFeatureStrategy>
): IStrategyPayload => {
return {
name: strategy.name,
constraints: strategy.constraints ?? [],
parameters: strategy.parameters ?? {},
};
};
export const formatFeaturePath = (
projectId: string,
featureId: string
): string => {
return `/projects/${projectId}/features/${featureId}`;
};
export const formatEditStrategyPath = (
projectId: string,
featureId: string,
environmentId: string,
strategyId: string
): string => {
const params = new URLSearchParams({ environmentId, strategyId });
return `/projects/${projectId}/features/${featureId}/strategies/edit?${params}`;
};
const formatUpdateStrategyApiCode = (
projectId: string,
featureId: string,
environmentId: string,
strategy: Partial<IFeatureStrategy>,
unleashUrl?: string
): string => {
if (!unleashUrl) {
return '';
}
const url = `${unleashUrl}/api/admin/projects/${projectId}/features/${featureId}/${environmentId}/development/strategies/${strategy.id}`;
const payload = JSON.stringify(strategy, undefined, 2);
return `curl --location --request PUT '${url}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${payload}'`;
};
export const featureStrategyHelp = `
An activation strategy will only run when a feature toggle is enabled and provides a way to control who will get access to the feature.
If any of a feature toggle's activation strategies returns true, the user will get access.
`;
export const featureStrategyDocsLink =
'https://docs.getunleash.io/user_guide/activation_strategy';