diff --git a/src/api/artifacts-api.js b/src/api/artifacts-api.js index 8dad748eab..31703bdced 100644 --- a/src/api/artifacts-api.js +++ b/src/api/artifacts-api.js @@ -55,7 +55,7 @@ const fetchArtifacts = (project, filters, config = {}, withLatestTag) => { const artifactsApi = { addTag: (project, tag, data) => mainHttpClient.put(`/projects/${project}/tags/${tag}`, data), buildFunction: data => mainHttpClient.post('/build/function', data), - deleteArtifact: (project, key, tag, tree) => { + deleteArtifact: (project, key, tag, tree, deletion_strategy, secrets) => { const config = { params: { tag, @@ -63,13 +63,18 @@ const artifactsApi = { } } + if (deletion_strategy) { + config.params.deletion_strategy = deletion_strategy + config.data = { secrets } + } + return mainHttpClientV2.delete(`/projects/${project}/artifacts/${key}`, config) }, deleteArtifacts: (project, name, category) => { const config = { params: {} } - + if (name) config.params.name = name if (category) config.params.category = category diff --git a/src/components/Files/files.util.js b/src/components/Files/files.util.js index 4b040c5bab..43bef9f077 100644 --- a/src/components/Files/files.util.js +++ b/src/components/Files/files.util.js @@ -19,6 +19,8 @@ such restriction. */ import React from 'react' +import DeleteArtifactPopUp from '../../elements/DeleteArtifactPopUp/DeleteArtifactPopUp' + import { ACTION_MENU_PARENT_ROW, ACTION_MENU_PARENT_ROW_EXPANDED, @@ -44,6 +46,7 @@ import { openDeleteConfirmPopUp } from 'igz-controls/utils/common.util' import { searchArtifactItem } from '../../utils/searchArtifactItem' import { setDownloadItem, setShowDownloadsList } from '../../reducers/downloadReducer' import { sortListByDate } from '../../utils' +import { openPopUp } from 'igz-controls/utils/common.util' import { ReactComponent as TagIcon } from 'igz-controls/images/tag-icon.svg' import { ReactComponent as YamlIcon } from 'igz-controls/images/yaml.svg' @@ -218,7 +221,7 @@ export const generateActionsMenu = ( handleAddTag, projectName, handleRefresh, - datasetsFilters, + filters, menuPosition ) => { const isTargetPathValid = getIsTargetPathValid(file ?? {}, frontendSpec) @@ -269,22 +272,13 @@ export const generateActionsMenu = ( : '', className: 'danger', onClick: () => - openDeleteConfirmPopUp( - 'Delete artifact?', - `Do you want to delete the artifact "${file.db_key}"? Deleted artifacts can not be restored.`, - () => { - handleDeleteArtifact( - dispatch, - projectName, - file.db_key, - file.tag, - file.tree, - handleRefresh, - datasetsFilters, - ARTIFACT_TYPE, - ) - } - ) + openPopUp(DeleteArtifactPopUp, { + artifact: file, + artifactType: ARTIFACT_TYPE, + category: ARTIFACT_OTHER_TYPE, + filters, + handleRefresh + }) }, { label: 'Delete all', @@ -303,7 +297,7 @@ export const generateActionsMenu = ( file.tag, file.tree, handleRefresh, - datasetsFilters, + filters, ARTIFACT_TYPE, ARTIFACT_OTHER_TYPE, true diff --git a/src/components/ModelsPage/Models/Models.js b/src/components/ModelsPage/Models/Models.js index 9c54392848..273d3d0df5 100644 --- a/src/components/ModelsPage/Models/Models.js +++ b/src/components/ModelsPage/Models/Models.js @@ -178,7 +178,7 @@ const Models = ({ fetchModelFeatureVector }) => { } }) }, - [dispatch] + [dispatch, params.projectName] ) const handleRefresh = useCallback( diff --git a/src/elements/DeleteArtifactPopUp/DeleteArtifactPopUp.js b/src/elements/DeleteArtifactPopUp/DeleteArtifactPopUp.js new file mode 100644 index 0000000000..2ff3f3d806 --- /dev/null +++ b/src/elements/DeleteArtifactPopUp/DeleteArtifactPopUp.js @@ -0,0 +1,159 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import React, { useCallback, useState } from 'react' +import { useDispatch } from 'react-redux' +import { useParams } from 'react-router-dom' +import { Form } from 'react-final-form' +import { createForm } from 'final-form' +import arrayMutators from 'final-form-arrays' +import PropTypes from 'prop-types' + +import { ConfirmDialog, FormRadio, FormKeyValueTable } from 'igz-controls/components' +import { TERTIARY_BUTTON, DANGER_BUTTON } from 'igz-controls/constants' + +import { handleDeleteArtifact } from '../../utils/handleDeleteArtifact' +import { getValidationRules } from 'igz-controls/utils/validation.util' +import { setFieldState } from 'igz-controls/utils/form.util' + +import './deleteArtifactPopUp.scss' + +const DeleteArtifactPopUp = ({ artifact, artifactType, category, filters, handleRefresh }) => { + const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(true) + const dispatch = useDispatch() + const params = useParams() + const formRef = React.useRef( + createForm({ + initialValues: { + deletion_strategy: 'metadata-only', + secrets: [] + }, + mutators: { ...arrayMutators, setFieldState }, + onSubmit: () => {} + }) + ) + + const handleCancel = () => { + setIsConfirmDialogOpen(false) + } + + const handleDelete = useCallback(() => { + const secrets = {} + + formRef.current.getState().values.secrets.forEach(secret => { + secrets[secret.data.key] = secret.data.value + }) + + handleDeleteArtifact( + dispatch, + params.projectName, + artifact.db_key, + artifact.tag, + artifact.tree, + handleRefresh, + filters, + artifactType, + category, + false, + formRef.current.getState().values.deletion_strategy, + secrets + ).then(() => { + setIsConfirmDialogOpen(false) + }) + }, [ + artifact.db_key, + artifact.tag, + artifact.tree, + artifactType, + category, + dispatch, + filters, + handleRefresh, + params.projectName + ]) + + return ( + handleCancel(), + label: 'Cancel', + variant: TERTIARY_BUTTON + }} + className="delete-artifact-pop-up" + confirmButton={{ + handler: () => handleDelete(), + label: 'Delete', + variant: DANGER_BUTTON + }} + header={`Delete ${artifactType}?`} + isOpen={isConfirmDialogOpen} + message={`Do you want to delete the dataset "${artifact.db_key}"? Deleted datasets can not be restored.`} + > +
{}}> + {formState => { + return ( + <> + + + +
+ {formState.values.deletion_strategy !== 'metadata-only' && ( + + )} +
+ + ) + }} + +
+ ) +} + +DeleteArtifactPopUp.propTypes = { + artifact: PropTypes.object.isRequired, + artifactType: PropTypes.string.isRequired, + category: PropTypes.string.isRequired, + filters: PropTypes.object.isRequired, + handleRefresh: PropTypes.func.isRequired +} + +export default DeleteArtifactPopUp diff --git a/src/elements/DeleteArtifactPopUp/deleteArtifactPopUp.scss b/src/elements/DeleteArtifactPopUp/deleteArtifactPopUp.scss new file mode 100644 index 0000000000..e89a5fb998 --- /dev/null +++ b/src/elements/DeleteArtifactPopUp/deleteArtifactPopUp.scss @@ -0,0 +1,5 @@ +.delete-artifact-pop-up { + .secrets-table { + margin-top: 20px; + } +} diff --git a/src/reducers/artifactsReducer.js b/src/reducers/artifactsReducer.js index 868f0be4f6..3207de29f9 100644 --- a/src/reducers/artifactsReducer.js +++ b/src/reducers/artifactsReducer.js @@ -87,9 +87,12 @@ export const addTag = createAsyncThunk('addTag', ({ project, tag, data }) => { export const buildFunction = createAsyncThunk('buildFunction', ({ funcData }) => { return artifactsApi.buildFunction(funcData) }) -export const deleteArtifact = createAsyncThunk('deleteArtifact', ({ project, key, tag, tree }) => { - return artifactsApi.deleteArtifact(project, key, tag, tree) -}) +export const deleteArtifact = createAsyncThunk( + 'deleteArtifact', + ({ project, key, tag, tree, deletion_strategy, secrets }) => { + return artifactsApi.deleteArtifact(project, key, tag, tree, deletion_strategy, secrets) + } +) export const deleteArtifacts = createAsyncThunk( 'deleteArtifacts', ({ project, name, category }) => { diff --git a/src/utils/handleDeleteArtifact.js b/src/utils/handleDeleteArtifact.js index 78a77e7f7d..f83cc3ab2b 100644 --- a/src/utils/handleDeleteArtifact.js +++ b/src/utils/handleDeleteArtifact.js @@ -33,17 +33,19 @@ export const handleDeleteArtifact = ( filters, artifactType, category, - isDeleteAll + isDeleteAll, + deletion_strategy, + secrets ) => { - dispatch( + return dispatch( isDeleteAll ? deleteArtifacts({ project, name: key, category }) - : deleteArtifact({ project, key, tag, tree }) + : deleteArtifact({ project, key, tag, tree, deletion_strategy, secrets }) ) .unwrap() .then(() => { refreshArtifacts(filters) - dispatch( + return dispatch( setNotification({ status: 200, id: Math.random(),