|
@@ -14,13 +14,15 @@
|
|
|
// See the License for the specific language governing permissions and
|
|
// See the License for the specific language governing permissions and
|
|
|
// limitations under the License.
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
-import React, { useState } from 'react';
|
|
|
|
|
|
|
+import React, { useCallback, useMemo, useState } from 'react';
|
|
|
import { Dropdown } from 'antd';
|
|
import { Dropdown } from 'antd';
|
|
|
import { MenuItemType } from 'antd/lib/menu/hooks/useItems';
|
|
import { MenuItemType } from 'antd/lib/menu/hooks/useItems';
|
|
|
|
|
|
|
|
import Button from 'cuix/dist/components/Button';
|
|
import Button from 'cuix/dist/components/Button';
|
|
|
import DropDownIcon from '@cloudera/cuix-core/icons/react/DropdownIcon';
|
|
import DropDownIcon from '@cloudera/cuix-core/icons/react/DropdownIcon';
|
|
|
import InfoIcon from '@cloudera/cuix-core/icons/react/InfoIcon';
|
|
import InfoIcon from '@cloudera/cuix-core/icons/react/InfoIcon';
|
|
|
|
|
+import DuplicateIcon from '@cloudera/cuix-core/icons/react/DuplicateIcon';
|
|
|
|
|
+import CopyClipboardIcon from '@cloudera/cuix-core/icons/react/CopyClipboardIcon';
|
|
|
|
|
|
|
|
import { i18nReact } from '../../../../utils/i18nReact';
|
|
import { i18nReact } from '../../../../utils/i18nReact';
|
|
|
import { StorageBrowserTableData } from '../../../../reactComponents/FileChooser/types';
|
|
import { StorageBrowserTableData } from '../../../../reactComponents/FileChooser/types';
|
|
@@ -38,59 +40,132 @@ import {
|
|
|
isS3,
|
|
isS3,
|
|
|
isOFSRoot
|
|
isOFSRoot
|
|
|
} from '../../../../utils/storageBrowserUtils';
|
|
} from '../../../../utils/storageBrowserUtils';
|
|
|
-import { rename } from '../../../../reactComponents/FileChooser/api';
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ RENAME_API_URL,
|
|
|
|
|
+ SET_REPLICATION_API_URL,
|
|
|
|
|
+ BULK_COPY_API_URL,
|
|
|
|
|
+ BULK_MOVE_API_URL
|
|
|
|
|
+} from '../../../../reactComponents/FileChooser/api';
|
|
|
import huePubSub from '../../../../utils/huePubSub';
|
|
import huePubSub from '../../../../utils/huePubSub';
|
|
|
|
|
+import useSaveData from '../../../../utils/hooks/useSaveData';
|
|
|
|
|
|
|
|
import SummaryModal from '../../SummaryModal/SummaryModal';
|
|
import SummaryModal from '../../SummaryModal/SummaryModal';
|
|
|
import InputModal from '../../InputModal/InputModal';
|
|
import InputModal from '../../InputModal/InputModal';
|
|
|
|
|
+import FileChooserModal from '../../FileChooserModal/FileChooserModal';
|
|
|
|
|
|
|
|
import './StorageBrowserActions.scss';
|
|
import './StorageBrowserActions.scss';
|
|
|
|
|
|
|
|
interface StorageBrowserRowActionsProps {
|
|
interface StorageBrowserRowActionsProps {
|
|
|
|
|
+ currentPath: string;
|
|
|
selectedFiles: StorageBrowserTableData[];
|
|
selectedFiles: StorageBrowserTableData[];
|
|
|
onSuccessfulAction: () => void;
|
|
onSuccessfulAction: () => void;
|
|
|
setLoadingFiles: (value: boolean) => void;
|
|
setLoadingFiles: (value: boolean) => void;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const StorageBrowserActions = ({
|
|
const StorageBrowserActions = ({
|
|
|
|
|
+ currentPath,
|
|
|
selectedFiles,
|
|
selectedFiles,
|
|
|
- setLoadingFiles,
|
|
|
|
|
- onSuccessfulAction
|
|
|
|
|
|
|
+ onSuccessfulAction,
|
|
|
|
|
+ setLoadingFiles
|
|
|
}: StorageBrowserRowActionsProps): JSX.Element => {
|
|
}: StorageBrowserRowActionsProps): JSX.Element => {
|
|
|
const [showSummaryModal, setShowSummaryModal] = useState<boolean>(false);
|
|
const [showSummaryModal, setShowSummaryModal] = useState<boolean>(false);
|
|
|
const [showRenameModal, setShowRenameModal] = useState<boolean>(false);
|
|
const [showRenameModal, setShowRenameModal] = useState<boolean>(false);
|
|
|
const [selectedFile, setSelectedFile] = useState<string>('');
|
|
const [selectedFile, setSelectedFile] = useState<string>('');
|
|
|
|
|
+ const [showReplicationModal, setShowReplicationModal] = useState<boolean>(false);
|
|
|
|
|
+ const [showCopyModal, setShowCopyModal] = useState<boolean>(false);
|
|
|
|
|
+ const [showMoveModal, setShowMoveModal] = useState<boolean>(false);
|
|
|
|
|
|
|
|
const { t } = i18nReact.useTranslation();
|
|
const { t } = i18nReact.useTranslation();
|
|
|
|
|
|
|
|
- const handleRename = (newName: string) => {
|
|
|
|
|
|
|
+ const { error: renameError, save: saveRename } = useSaveData(RENAME_API_URL);
|
|
|
|
|
+
|
|
|
|
|
+ const handleRename = (value: string) => {
|
|
|
setLoadingFiles(true);
|
|
setLoadingFiles(true);
|
|
|
- rename(selectedFile, newName)
|
|
|
|
|
- .then(() => {
|
|
|
|
|
|
|
+ saveRename(
|
|
|
|
|
+ { source_path: selectedFile, destination_path: value },
|
|
|
|
|
+ {
|
|
|
|
|
+ onSuccess: () => {
|
|
|
|
|
+ setLoadingFiles(false);
|
|
|
|
|
+ onSuccessfulAction();
|
|
|
|
|
+ },
|
|
|
|
|
+ onError: () => {
|
|
|
|
|
+ huePubSub.publish('hue.error', renameError);
|
|
|
|
|
+ setLoadingFiles(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const { error: replicationError, save: saveReplication } = useSaveData(SET_REPLICATION_API_URL);
|
|
|
|
|
+ const handleReplication = (replicationFactor: number) => {
|
|
|
|
|
+ saveReplication(
|
|
|
|
|
+ { path: selectedFile, replication_factor: replicationFactor },
|
|
|
|
|
+ {
|
|
|
|
|
+ onSuccess: () => onSuccessfulAction(),
|
|
|
|
|
+ onError: () => {
|
|
|
|
|
+ huePubSub.publish('hue.error', replicationError);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const { error: bulkCopyError, save: saveBulkCopy } = useSaveData(BULK_COPY_API_URL, {
|
|
|
|
|
+ postOptions: {
|
|
|
|
|
+ qsEncodeData: false,
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Content-Type': 'multipart/form-data'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const handleCopy = (destination_path: string) => {
|
|
|
|
|
+ setLoadingFiles(true);
|
|
|
|
|
+ const formData = new FormData();
|
|
|
|
|
+ selectedFiles.map(selectedFile => {
|
|
|
|
|
+ formData.append('source_path', selectedFile.path);
|
|
|
|
|
+ });
|
|
|
|
|
+ formData.append('destination_path', destination_path);
|
|
|
|
|
+ saveBulkCopy(formData, {
|
|
|
|
|
+ onSuccess: () => {
|
|
|
|
|
+ setLoadingFiles(false);
|
|
|
onSuccessfulAction();
|
|
onSuccessfulAction();
|
|
|
- })
|
|
|
|
|
- .catch(error => {
|
|
|
|
|
- huePubSub.publish('hue.error', error);
|
|
|
|
|
- setShowRenameModal(false);
|
|
|
|
|
- })
|
|
|
|
|
- .finally(() => {
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ onError: () => {
|
|
|
|
|
+ huePubSub.publish('hue.error', bulkCopyError);
|
|
|
setLoadingFiles(false);
|
|
setLoadingFiles(false);
|
|
|
- });
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const isSummaryEnabled = () => {
|
|
|
|
|
- if (selectedFiles.length !== 1) {
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ const { error: bulkMoveError, save: saveBulkMove } = useSaveData(BULK_MOVE_API_URL, {
|
|
|
|
|
+ postOptions: {
|
|
|
|
|
+ qsEncodeData: false,
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Content-Type': 'multipart/form-data'
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- const selectedFile = selectedFiles[0];
|
|
|
|
|
- return (isHDFS(selectedFile.path) || isOFS(selectedFile.path)) && selectedFile.type === 'file';
|
|
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const handleMove = (destination_path: string) => {
|
|
|
|
|
+ setLoadingFiles(true);
|
|
|
|
|
+ const formData = new FormData();
|
|
|
|
|
+ selectedFiles.map(selectedFile => {
|
|
|
|
|
+ formData.append('source_path', selectedFile.path);
|
|
|
|
|
+ });
|
|
|
|
|
+ formData.append('destination_path', destination_path);
|
|
|
|
|
+ saveBulkMove(formData, {
|
|
|
|
|
+ onSuccess: () => {
|
|
|
|
|
+ setLoadingFiles(false);
|
|
|
|
|
+ onSuccessfulAction();
|
|
|
|
|
+ },
|
|
|
|
|
+ onError: () => {
|
|
|
|
|
+ huePubSub.publish('hue.error', bulkMoveError);
|
|
|
|
|
+ setLoadingFiles(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const isRenameEnabled = () => {
|
|
|
|
|
- if (selectedFiles.length !== 1) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- const selectedFilePath = selectedFiles[0].path;
|
|
|
|
|
|
|
+ const isValidFileOrFolder = (selectedFilePath: string) => {
|
|
|
return (
|
|
return (
|
|
|
isHDFS(selectedFilePath) ||
|
|
isHDFS(selectedFilePath) ||
|
|
|
(isS3(selectedFilePath) && !isS3Root(selectedFilePath)) ||
|
|
(isS3(selectedFilePath) && !isS3Root(selectedFilePath)) ||
|
|
@@ -103,10 +178,72 @@ const StorageBrowserActions = ({
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const getActions = () => {
|
|
|
|
|
|
|
+ const isSummaryEnabled = useMemo(() => {
|
|
|
|
|
+ if (selectedFiles.length !== 1) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ const selectedFile = selectedFiles[0];
|
|
|
|
|
+ return (isHDFS(selectedFile.path) || isOFS(selectedFile.path)) && selectedFile.type === 'file';
|
|
|
|
|
+ }, [selectedFiles]);
|
|
|
|
|
+
|
|
|
|
|
+ const isRenameEnabled = useMemo(() => {
|
|
|
|
|
+ if (selectedFiles.length !== 1) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ const selectedFilePath = selectedFiles[0].path;
|
|
|
|
|
+ return isValidFileOrFolder(selectedFilePath);
|
|
|
|
|
+ }, [selectedFiles]);
|
|
|
|
|
+
|
|
|
|
|
+ const isReplicationEnabled = useMemo(() => {
|
|
|
|
|
+ if (selectedFiles.length !== 1) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ const selectedFile = selectedFiles[0];
|
|
|
|
|
+ return isHDFS(selectedFile.path) && selectedFile.type === 'file';
|
|
|
|
|
+ }, [selectedFiles]);
|
|
|
|
|
+
|
|
|
|
|
+ const isCopyEnabled = useMemo(() => {
|
|
|
|
|
+ if (selectedFiles.length > 0) {
|
|
|
|
|
+ const selectedFilePath = selectedFiles[0].path;
|
|
|
|
|
+ return isValidFileOrFolder(selectedFilePath);
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }, [selectedFiles]);
|
|
|
|
|
+
|
|
|
|
|
+ const isMoveEnabled = useMemo(() => {
|
|
|
|
|
+ if (selectedFiles.length > 0) {
|
|
|
|
|
+ const selectedFilePath = selectedFiles[0].path;
|
|
|
|
|
+ return isValidFileOrFolder(selectedFilePath);
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }, [selectedFiles]);
|
|
|
|
|
+
|
|
|
|
|
+ const getActions = useCallback(() => {
|
|
|
const actions: MenuItemType[] = [];
|
|
const actions: MenuItemType[] = [];
|
|
|
if (selectedFiles && selectedFiles.length > 0 && !inTrash(selectedFiles[0].path)) {
|
|
if (selectedFiles && selectedFiles.length > 0 && !inTrash(selectedFiles[0].path)) {
|
|
|
- if (isSummaryEnabled()) {
|
|
|
|
|
|
|
+ if (isCopyEnabled) {
|
|
|
|
|
+ actions.push({
|
|
|
|
|
+ key: 'copy',
|
|
|
|
|
+ icon: <CopyClipboardIcon />,
|
|
|
|
|
+ label: t('Copy'),
|
|
|
|
|
+ onClick: () => {
|
|
|
|
|
+ setSelectedFile(selectedFiles[0].path);
|
|
|
|
|
+ setShowCopyModal(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isMoveEnabled) {
|
|
|
|
|
+ actions.push({
|
|
|
|
|
+ key: 'move',
|
|
|
|
|
+ icon: <CopyClipboardIcon />,
|
|
|
|
|
+ label: t('Move'),
|
|
|
|
|
+ onClick: () => {
|
|
|
|
|
+ setSelectedFile(selectedFiles[0].path);
|
|
|
|
|
+ setShowMoveModal(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isSummaryEnabled) {
|
|
|
actions.push({
|
|
actions.push({
|
|
|
key: 'content_summary',
|
|
key: 'content_summary',
|
|
|
icon: <InfoIcon />,
|
|
icon: <InfoIcon />,
|
|
@@ -117,7 +254,7 @@ const StorageBrowserActions = ({
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
- if (isRenameEnabled()) {
|
|
|
|
|
|
|
+ if (isRenameEnabled) {
|
|
|
actions.push({
|
|
actions.push({
|
|
|
key: 'rename',
|
|
key: 'rename',
|
|
|
icon: <InfoIcon />,
|
|
icon: <InfoIcon />,
|
|
@@ -128,9 +265,27 @@ const StorageBrowserActions = ({
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
+ if (isReplicationEnabled) {
|
|
|
|
|
+ actions.push({
|
|
|
|
|
+ key: 'setReplication',
|
|
|
|
|
+ icon: <DuplicateIcon />,
|
|
|
|
|
+ label: t('Set Replication'),
|
|
|
|
|
+ onClick: () => {
|
|
|
|
|
+ setSelectedFile(selectedFiles[0].path);
|
|
|
|
|
+ setShowReplicationModal(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
return actions;
|
|
return actions;
|
|
|
- };
|
|
|
|
|
|
|
+ }, [
|
|
|
|
|
+ selectedFiles,
|
|
|
|
|
+ isSummaryEnabled,
|
|
|
|
|
+ isRenameEnabled,
|
|
|
|
|
+ isReplicationEnabled,
|
|
|
|
|
+ isCopyEnabled,
|
|
|
|
|
+ currentPath
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<>
|
|
<>
|
|
@@ -140,7 +295,8 @@ const StorageBrowserActions = ({
|
|
|
items: getActions(),
|
|
items: getActions(),
|
|
|
className: 'hue-storage-browser__table-actions-menu'
|
|
className: 'hue-storage-browser__table-actions-menu'
|
|
|
}}
|
|
}}
|
|
|
- trigger={['click', 'hover']}
|
|
|
|
|
|
|
+ trigger={['click']}
|
|
|
|
|
+ disabled={getActions().length === 0 ? true : false}
|
|
|
>
|
|
>
|
|
|
<Button data-event="">
|
|
<Button data-event="">
|
|
|
{t('Actions')}
|
|
{t('Actions')}
|
|
@@ -159,6 +315,34 @@ const StorageBrowserActions = ({
|
|
|
showModal={showRenameModal}
|
|
showModal={showRenameModal}
|
|
|
onSubmit={handleRename}
|
|
onSubmit={handleRename}
|
|
|
onClose={() => setShowRenameModal(false)}
|
|
onClose={() => setShowRenameModal(false)}
|
|
|
|
|
+ inputType="text"
|
|
|
|
|
+ initialValue={selectedFiles[0]?.name}
|
|
|
|
|
+ />
|
|
|
|
|
+ <InputModal
|
|
|
|
|
+ title={'Setting Replication factor for: ' + selectedFile}
|
|
|
|
|
+ inputLabel={t('Replication factor:')}
|
|
|
|
|
+ submitText={t('Submit')}
|
|
|
|
|
+ showModal={showReplicationModal}
|
|
|
|
|
+ onSubmit={handleReplication}
|
|
|
|
|
+ onClose={() => setShowReplicationModal(false)}
|
|
|
|
|
+ inputType="number"
|
|
|
|
|
+ initialValue={selectedFiles[0]?.replication}
|
|
|
|
|
+ />
|
|
|
|
|
+ <FileChooserModal
|
|
|
|
|
+ onClose={() => setShowCopyModal(false)}
|
|
|
|
|
+ onSubmit={handleCopy}
|
|
|
|
|
+ showModal={showCopyModal}
|
|
|
|
|
+ title="Copy to"
|
|
|
|
|
+ sourcePath={currentPath}
|
|
|
|
|
+ submitText={t('Copy')}
|
|
|
|
|
+ />
|
|
|
|
|
+ <FileChooserModal
|
|
|
|
|
+ onClose={() => setShowMoveModal(false)}
|
|
|
|
|
+ onSubmit={handleMove}
|
|
|
|
|
+ showModal={showMoveModal}
|
|
|
|
|
+ title="Move to"
|
|
|
|
|
+ sourcePath={currentPath}
|
|
|
|
|
+ submitText={t('Move')}
|
|
|
/>
|
|
/>
|
|
|
</>
|
|
</>
|
|
|
);
|
|
);
|