1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-28 00:06:53 +01:00

fix: add clone feature toggle via API

This commit is contained in:
Ivar Conradi Østhus 2021-10-07 23:04:14 +02:00
parent 326b14fcec
commit 7406cbbaa7
4 changed files with 60 additions and 53 deletions

View File

@ -19,31 +19,23 @@ import { trim } from '../../../common/util';
import ConditionallyRender from '../../../common/ConditionallyRender'; import ConditionallyRender from '../../../common/ConditionallyRender';
import { Alert } from '@material-ui/lab'; import { Alert } from '@material-ui/lab';
import { getTogglePath } from '../../../../utils/route-path-helpers'; import { getTogglePath } from '../../../../utils/route-path-helpers';
import useFeatureApi from '../../../../hooks/api/actions/useFeatureApi/useFeatureApi';
import useFeature from '../../../../hooks/api/getters/useFeature/useFeature';
const CopyFeature = props => { const CopyFeature = props => {
// static displayName = `AddFeatureComponent-${getDisplayName(Component)}`; // static displayName = `AddFeatureComponent-${getDisplayName(Component)}`;
const [replaceGroupId, setReplaceGroupId] = useState(true); const [replaceGroupId, setReplaceGroupId] = useState(true);
const [apiError, setApiError] = useState(''); const [apiError, setApiError] = useState('');
const [copyToggle, setCopyToggle] = useState();
const [nameError, setNameError] = useState(undefined); const [nameError, setNameError] = useState(undefined);
const [newToggleName, setNewToggleName] = useState(); const [newToggleName, setNewToggleName] = useState();
const { cloneFeatureToggle } = useFeatureApi();
const inputRef = useRef(); const inputRef = useRef();
const { name } = useParams(); const { name: copyToggleName, id: projectId } = useParams();
const copyToggleName = name; const { feature } = useFeature(projectId, copyToggleName);
const { features } = props;
useEffect(() => { useEffect(() => {
const copyToggle = features.find(item => item.name === copyToggleName);
if (copyToggle) {
setCopyToggle(copyToggle);
inputRef.current?.focus(); inputRef.current?.focus();
} else { }, []);
props.fetchFeatureToggles();
}
/* eslint-disable-next-line */
}, [features.length]);
const setValue = evt => { const setValue = evt => {
const value = trim(evt.target.value); const value = trim(evt.target.value);
@ -71,31 +63,21 @@ const CopyFeature = props => {
return; return;
} }
const { history } = props;
copyToggle.name = newToggleName;
if (replaceGroupId) {
copyToggle.strategies.forEach(s => {
if (s.parameters && s.parameters.groupId) {
s.parameters.groupId = newToggleName;
}
});
}
try { try {
props await cloneFeatureToggle(
.createFeatureToggle(copyToggle) projectId,
.then(() => copyToggleName,
history.push( { name: newToggleName, replaceGroupId }
getTogglePath(copyToggle.project, copyToggle.name)
)
); );
props.history.push(
getTogglePath(projectId, newToggleName)
)
} catch (e) { } catch (e) {
setApiError(e); setApiError(e);
} }
}; };
if (!copyToggle) return <span>Toggle not found</span>; if (!feature || !feature.name) return <span>Toggle not found</span>;
return ( return (
<Paper <Paper
@ -103,7 +85,7 @@ const CopyFeature = props => {
style={{ overflow: 'visible' }} style={{ overflow: 'visible' }}
> >
<div className={styles.header}> <div className={styles.header}>
<h1>Copy&nbsp;{copyToggle.name}</h1> <h1>Copy&nbsp;{copyToggleName}</h1>
</div> </div>
<ConditionallyRender <ConditionallyRender
condition={apiError} condition={apiError}
@ -114,9 +96,9 @@ const CopyFeature = props => {
You are about to create a new feature toggle by cloning the You are about to create a new feature toggle by cloning the
configuration of feature toggle&nbsp; configuration of feature toggle&nbsp;
<Link <Link
to={getTogglePath(copyToggle.project, copyToggle.name)} to={getTogglePath(projectId, copyToggleName)}
> >
{copyToggle.name} {copyToggleName}
</Link> </Link>
. You must give the new feature toggle a unique name before . You must give the new feature toggle a unique name before
you can proceed. you can proceed.
@ -157,10 +139,7 @@ const CopyFeature = props => {
}; };
CopyFeature.propTypes = { CopyFeature.propTypes = {
copyToggle: PropTypes.object,
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
createFeatureToggle: PropTypes.func.isRequired,
fetchFeatureToggles: PropTypes.func.isRequired,
validateName: PropTypes.func.isRequired, validateName: PropTypes.func.isRequired,
}; };

View File

@ -1,24 +1,15 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import CopyFeatureComponent from './CopyFeature'; import CopyFeatureComponent from './CopyFeature';
import { import {
createFeatureToggles, validateName
validateName,
fetchFeatureToggles,
} from '../../../../store/feature-toggle/actions'; } from '../../../../store/feature-toggle/actions';
const mapStateToProps = (state, props) => ({ const mapStateToProps = (state, props) => ({
history: props.history, history: props.history,
features: state.features.toJS(),
copyToggle: state.features
.toJS()
.find(toggle => toggle.name === props.copyToggleName),
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
validateName, validateName,
createFeatureToggle: featureToggle =>
createFeatureToggles(featureToggle)(dispatch),
fetchFeatureToggles: () => fetchFeatureToggles()(dispatch),
}); });
const FormAddContainer = connect( const FormAddContainer = connect(

View File

@ -143,6 +143,26 @@ const useFeatureApi = () => {
} }
}; };
const cloneFeatureToggle = async (
projectId: string,
featureId: string,
payload: {name: string, replaceGroupId: boolean}
) => {
const path = `api/admin/projects/${projectId}/features/${featureId}/clone`;
const req = createRequest(
path,
{ method: 'POST', body: JSON.stringify(payload) },
);
try {
const res = await makeRequest(req.caller, req.id);
return res;
} catch (e) {
throw e;
}
};
return { return {
changeFeatureProject, changeFeatureProject,
errors, errors,
@ -152,6 +172,7 @@ const useFeatureApi = () => {
deleteTagFromFeature, deleteTagFromFeature,
archiveFeatureToggle, archiveFeatureToggle,
patchFeatureToggle, patchFeatureToggle,
cloneFeatureToggle
}; };
}; };

View File

@ -17,13 +17,29 @@ const useFeature = (
id: string, id: string,
options: IUseFeatureOptions = {} options: IUseFeatureOptions = {}
) => { ) => {
const fetcher = () => { const fetcher = async () => {
const path = formatApiPath( const path = formatApiPath(
`api/admin/projects/${projectId}/features/${id}` `api/admin/projects/${projectId}/features/${id}`
); );
return fetch(path, {
const res = await fetch(path, {
method: 'GET', method: 'GET',
}).then(res => res.json()); });
// If the status code is not in the range 200-299,
// we still try to parse and throw it.
if (!res.ok) {
const error = new Error('An error occurred while fetching the data.')
// Attach extra info to the error object.
// @ts-ignore
error.info = await res.json();
// @ts-ignore
error.status = res.status;
throw error;
}
return res.json()
}; };
const FEATURE_CACHE_KEY = `api/admin/projects/${projectId}/features/${id}`; const FEATURE_CACHE_KEY = `api/admin/projects/${projectId}/features/${id}`;