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:
parent
326b14fcec
commit
7406cbbaa7
@ -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 {copyToggle.name}</h1>
|
<h1>Copy {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
|
configuration of feature toggle
|
||||||
<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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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}`;
|
||||||
|
Loading…
Reference in New Issue
Block a user