diff --git a/src/components/CreateFeed/createFeedHelper.ts b/src/components/CreateFeed/createFeedHelper.ts index 2633db222..f8f7201b0 100644 --- a/src/components/CreateFeed/createFeedHelper.ts +++ b/src/components/CreateFeed/createFeedHelper.ts @@ -76,12 +76,10 @@ export const createFeedInstanceWithDircopy = async ( state: PipelineState, ) => { const { chrisFiles, localFiles } = data; - - let dirpath: string[] = []; - let feed: Feed; + const dirpath: string[] = []; if (selectedConfig.includes("swift_storage")) { - dirpath = chrisFiles.map((path: string) => path); + dirpath.push(...chrisFiles); } if (selectedConfig.includes("local_select")) { @@ -92,8 +90,8 @@ export const createFeedInstanceWithDircopy = async ( try { await uploadLocalFiles(localFiles, path, setUploadFileCallback); } catch (error) { - const errObj = catchError(error); - errorCallback(errObj); + errorCallback(catchError(error)); + return null; } } @@ -101,61 +99,48 @@ export const createFeedInstanceWithDircopy = async ( const client = ChrisAPIClient.getClient(); const dircopy = await getPlugin("pl-dircopy"); - if (dircopy instanceof Plugin) { - try { - const createdInstance = await client.createPluginInstance( - dircopy.data.id, - { - //@ts-ignore - dir: dirpath.join(","), - }, - ); - const { pipelineToAdd, computeInfo, titleInfo, selectedPipeline } = - state; - - const id = pipelineToAdd?.data.id; - const resources = selectedPipeline?.[id]; - if (createdInstance) { - if (resources) { - const { parameters } = resources; - - const nodes_info = client.computeWorkflowNodesInfo(parameters.data); - - for (const node of nodes_info) { - // Set compute info - const activeNode = computeInfo?.[id][node.piping_id]; - // Set Title - const titleSet = titleInfo?.[id][node.piping_id]; - - if (activeNode) { - const compute_node = activeNode.currentlySelected; - node.compute_resource_name = compute_node; - } - - if (titleSet) { - node.title = titleSet; - } - } - - await client.createWorkflow(id, { - previous_plugin_inst_id: createdInstance.data.id, - nodes_info: JSON.stringify(nodes_info), - }); - } + if (!(dircopy instanceof Plugin)) { + return null; + } + + const createdInstance = await client.createPluginInstance(dircopy.data.id, { + //@ts-ignore + dir: dirpath.join(","), + }); + + const { pipelineToAdd, computeInfo, titleInfo, selectedPipeline } = state; + const id = pipelineToAdd?.data.id; + const resources = selectedPipeline?.[id]; + + if (createdInstance && resources) { + const { parameters } = resources; + const nodes_info = client.computeWorkflowNodesInfo(parameters.data); - feed = (await createdInstance.getFeed()) as Feed; - return feed; + for (const node of nodes_info) { + const activeNode = computeInfo?.[id][node.piping_id]; + const titleSet = titleInfo?.[id][node.piping_id]; + + if (activeNode) { + node.compute_resource_name = activeNode.currentlySelected; + } + + if (titleSet) { + node.title = titleSet; } - } catch (error) { - console.log("Error", error); } - return null; - //when the `post` finishes, the dircopyInstances's internal collection is updated + await client.createWorkflow(id, { + previous_plugin_inst_id: createdInstance.data.id, + nodes_info: JSON.stringify(nodes_info), + }); + + return (await createdInstance.getFeed()) as Feed; } + + return null; } catch (error) { - const errorObj = catchError(error); - errorCallback(errorObj); + errorCallback(catchError(error)); + return null; } }; @@ -165,7 +150,7 @@ export const createFeedInstanceWithFS = async ( selectedPlugin: Plugin | undefined, errorCallback: (error: any) => void, ) => { - let feed; + let feed: Feed | null = null; if (selectedPlugin) { if (selectedPlugin instanceof Plugin) { const data = await getRequiredObject( diff --git a/src/components/Feeds/FeedListView.tsx b/src/components/Feeds/FeedListView.tsx index 7a07b5a7f..8ba628f06 100644 --- a/src/components/Feeds/FeedListView.tsx +++ b/src/components/Feeds/FeedListView.tsx @@ -237,7 +237,7 @@ const TableSelectable: React.FunctionComponent = () => { search={search} /> - {feedsToDisplay && } + {feedsToDisplay && } {isLoading || isFetching || diff --git a/src/components/IconContainer/index.tsx b/src/components/IconContainer/index.tsx index 7c22f480c..843610fac 100644 --- a/src/components/IconContainer/index.tsx +++ b/src/components/IconContainer/index.tsx @@ -1,31 +1,29 @@ -import React, { ReactElement } from "react"; +import { Feed } from "@fnndsc/chrisapi"; import { - ToggleGroup, - ToggleGroupItem, - Tooltip, - Modal, - ModalVariant, + Button, + Checkbox, Form, FormGroup, + Modal, + ModalVariant, TextInput, - Button, - Checkbox, + ToggleGroup, + ToggleGroupItem, + Tooltip, } from "@patternfly/react-core"; -import { Alert } from "antd"; -import { cujs } from "chris-utility"; +import MdCallSplit from "@patternfly/react-icons/dist/esm/icons/code-branch-icon"; import FaDownload from "@patternfly/react-icons/dist/esm/icons/download-icon"; -import FaTrash from "@patternfly/react-icons/dist/esm/icons/trash-icon"; - -import { ShareButtonIcon } from "../../icons"; import MdIosShare from "@patternfly/react-icons/dist/esm/icons/share-icon"; -import MdCallSplit from "@patternfly/react-icons/dist/esm/icons/code-branch-icon"; +import FaTrash from "@patternfly/react-icons/dist/esm/icons/trash-icon"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { Alert } from "antd"; +import { cujs } from "chris-utility"; +import React, { ReactElement } from "react"; import { useDispatch } from "react-redux"; +import ChrisAPIClient from "../../api/chrisapiclient"; +import { ShareButtonIcon } from "../../icons"; import { setBulkSelect } from "../../store/feed/actions"; import { useTypedSelector } from "../../store/hooks"; -import { Feed } from "@fnndsc/chrisapi"; - -import ChrisAPIClient from "../../api/chrisapiclient"; -import { useQueryClient } from "@tanstack/react-query"; function capitalizeFirstLetter(stringLetter: string) { return stringLetter.charAt(0).toUpperCase() + stringLetter.slice(1); @@ -36,7 +34,7 @@ interface ModalState { feedName: string; currentAction: string; modalDescription: string; - errorHandling: Record; + errorHandling: string; sharePublically: boolean; } @@ -46,12 +44,12 @@ const getInitialState = () => { feedName: "", currentAction: "", modalDescription: "", - errorHandling: {}, + errorHandling: "", sharePublically: false, }; }; -const IconContainer = ({ allFeeds }: { allFeeds: Feed[] }) => { +const IconContainer = () => { const queryClient = useQueryClient(); const { bulkSelect } = useTypedSelector((state) => { return state.feed; @@ -60,7 +58,8 @@ const IconContainer = ({ allFeeds }: { allFeeds: Feed[] }) => { const [modalState, setModalState] = React.useState(getInitialState); - const { currentAction, isOpen, errorHandling, feedName } = modalState; + const { currentAction, isOpen, errorHandling, sharePublically, feedName } = + modalState; const getDefaultName = (bulkSelect: Feed[], action: string) => { if (bulkSelect.length > 0) { @@ -70,9 +69,9 @@ const IconContainer = ({ allFeeds }: { allFeeds: Feed[] }) => { : "Enter a name for your new feed (optional)"; let prefix = ""; - if (action == "merge") { + if (action === "merge") { prefix = "Merge of "; - } else if (action == "download") { + } else if (action === "download") { prefix = "archive-"; } else { prefix = ""; @@ -85,11 +84,11 @@ const IconContainer = ({ allFeeds }: { allFeeds: Feed[] }) => { newFeedName = feedNames.toString().replace(/[, ]+/g, "_"); newFeedName = prefix + newFeedName; newFeedName = newFeedName.substring(0, 100); - if (action == "duplicate") { + if (action === "duplicate") { if (bulkSelect.length > 1) { newFeedName = "duplicate-"; } else { - newFeedName = "duplicate-" + bulkSelect[0].data.name; + newFeedName = `duplicate-${bulkSelect[0].data.name}`; } } } @@ -113,9 +112,7 @@ const IconContainer = ({ allFeeds }: { allFeeds: Feed[] }) => { } else { setModalState({ ...modalState, - errorHandling: { - value: ["Please select a feed for this operation"], - }, + errorHandling: "Please select a feed for this operation", isOpen: value, }); } @@ -131,153 +128,174 @@ const IconContainer = ({ allFeeds }: { allFeeds: Feed[] }) => { }); }; - const handleDelete = async (bulkSelect: Feed[]) => { - if (bulkSelect && allFeeds) { + const deleteFeedMutation = useMutation({ + mutationFn: async (bulkSelect: Feed[]) => { for (const feed of bulkSelect) { try { await feed.delete(); - } catch (error: any) { - const errorMessage = error.response - ? error.response.data - : error.message; - handleError(errorMessage); + } catch (e: any) { + throw new Error(e.message); } } - + }, + onSuccess: () => { dispatch(setBulkSelect([], false)); queryClient.invalidateQueries({ queryKey: ["feeds"], }); handleModalToggle(false); - } - }; + }, + onError: (error: { message: string }) => { + handleError(error.message); + }, + }); - const handleShare = async (bulkSelect: Feed[]) => { - try { - if (modalState.sharePublically) { - bulkSelect.map(async (feed) => { + const shareFeedMutation = useMutation({ + mutationFn: async (data: { + bulkSelect: Feed[]; + sharePublically: boolean; + feedName: string; + }) => { + const { bulkSelect, sharePublically, feedName } = data; + for (const feed of bulkSelect) { + try { await feed.put({ //@ts-ignore - public: true, - }); - }); - } else { - bulkSelect.map(async (feed) => { - await feed.put({ - owner: feedName, + public: sharePublically, + owner: sharePublically ? undefined : feedName, }); - }); + } catch (error: any) { + throw new Error(error); + } } + }, + onSuccess: () => { + dispatch(setBulkSelect([], false)); + handleModalToggle(false); + }, + onError: (error) => { + handleError(error.message); + }, + }); - setModalState(getInitialState()); - } catch (error: any) { - handleError(error.response.data || error.message); - } - }; - - const handleError = (errorMessage: any) => { - setModalState({ - ...modalState, - errorHandling: errorMessage, - }); - }; + const handleDownloadMutation = useMutation({ + mutationFn: async (data: { + feedList: Feed[]; + feedName: string; + operation: string; + }) => { + const { feedList, feedName, operation } = data; - const handleDownloadFeed = async ( - feedList: Feed[], - feedName: string, - operation: string, - ) => { - const client = ChrisAPIClient.getClient(); - cujs.setClient(client); - const feedIdList = []; - const feedNames = []; - for (let i = 0; i < feedList.length; i++) { - const data = feedList[i].data; - feedIdList.push(data.id); - feedNames.push(data.name); - } + const client = ChrisAPIClient.getClient(); + cujs.setClient(client); + const feedIdList = []; + const feedNames = []; + for (let i = 0; i < feedList.length; i++) { + const data = feedList[i].data; + feedIdList.push(data.id); + feedNames.push(data.name); + } - try { - // truncate name of the merged feed(limit=100) + // Truncate the name of the merged feed (limit=100) let newFeedName = feedNames.toString().replace(/[, ]+/g, "_"); - let createdFeed; + let createdFeed: Feed | null = null; if (operation === "download") { newFeedName = `archive-${newFeedName}`; newFeedName = newFeedName.substring(0, 100); + newFeedName = feedName === "" ? newFeedName : feedName; - newFeedName = feedName == "" ? newFeedName : feedName; - createdFeed = await cujs.downloadMultipleFeeds(feedIdList, newFeedName); + try { + createdFeed = await cujs.downloadMultipleFeeds( + feedIdList, + newFeedName, + ); + } catch (e: any) { + throw new Error(e); + // Not throwing the error here to allow handling in the onError callback + } } if (operation === "merge") { newFeedName = `merge-${newFeedName}`; newFeedName = newFeedName.substring(0, 100); - newFeedName = feedName == "" ? newFeedName : feedName; - createdFeed = await cujs.mergeMultipleFeeds(feedIdList, newFeedName); - } + newFeedName = feedName === "" ? newFeedName : feedName; - if (createdFeed) { - queryClient.invalidateQueries({ - queryKey: ["feeds"], - }); - setModalState({ - ...modalState, - isOpen: false, - }); + try { + createdFeed = await cujs.mergeMultipleFeeds(feedIdList, newFeedName); + } catch (e: any) { + throw new Error(e); + } } - } catch (error: any) { - const errorMessage = error.response ? error.response.data : error.message; - handleError(errorMessage); - } - }; - - const handleDuplicateFeed = async (feedList: Feed[], feedName: string) => { - const client = ChrisAPIClient.getClient(); - cujs.setClient(client); - for (let i = 0; i < feedList.length; i++) { - const feedIdList = []; - const data = feedList[i].data; - const newFeedName = feedName - ? `${feedName}-${data.name}` - : `duplicate-${data.name}`; - feedIdList.push(data.id); - try { - const createdFeed: Feed = await cujs.mergeMultipleFeeds( - feedIdList, - newFeedName, - ); - if (createdFeed) { - queryClient.invalidateQueries({ - queryKey: ["feeds"], - }); - setModalState({ - ...modalState, - isOpen: false, - }); + return createdFeed; // Return null if operation is neither "download" nor "merge" + }, + onSuccess: () => { + // Handle success actions if needed + queryClient.invalidateQueries({ + queryKey: ["feeds"], + }); + handleModalToggle(false); + }, + onError: (error: Error) => { + handleError(error.message); + }, + }); + const handleDuplicateFeedMutation = useMutation({ + mutationFn: async (data: { feedList: Feed[]; feedName: string }) => { + const { feedList, feedName } = data; + const client = ChrisAPIClient.getClient(); + cujs.setClient(client); + for (let i = 0; i < feedList.length; i++) { + const feedIdList = []; + const data = feedList[i].data; + const newFeedName = feedName + ? `${feedName}-${data.name}` + : `duplicate-${data.name}`; + feedIdList.push(data.id); + try { + await cujs.mergeMultipleFeeds(feedIdList, newFeedName); + } catch (error: any) { + throw new Error(error); } - } catch (error: any) { - const errorMessage = error.response - ? error.response.data - : error.message; - handleError(errorMessage); } - } + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["feeds"], + }); + handleModalToggle(false); + }, + onError: (error) => { + handleError(error.message); + }, + }); + + const handleError = (errorMessage: string) => { + setModalState({ + ...modalState, + errorHandling: errorMessage, + }); }; const handleSubmit = () => { - currentAction === "share" && handleShare(bulkSelect); + currentAction === "share" && + shareFeedMutation.mutate({ bulkSelect, sharePublically, feedName }); + currentAction === "delete" && deleteFeedMutation.mutate(bulkSelect); currentAction === "download" && - handleDownloadFeed(bulkSelect, feedName, "download"); + handleDownloadMutation.mutate({ + feedList: bulkSelect, + feedName, + operation: "download", + }); currentAction === "merge" && - handleDownloadFeed(bulkSelect, feedName, "merge"); - currentAction === "delete" && handleDelete(bulkSelect); - currentAction === "duplicate" && handleDuplicateFeed(bulkSelect, feedName); - }; - - const alert = (_error: any) => { - return ; + handleDownloadMutation.mutate({ + feedList: bulkSelect, + feedName, + operation: "merge", + }); + currentAction === "duplicate" && + handleDuplicateFeedMutation.mutate({ feedList: bulkSelect, feedName }); }; return ( @@ -371,11 +389,9 @@ const IconContainer = ({ allFeeds }: { allFeeds: Feed[] }) => { )} - {Object.keys(errorHandling).length > 0 && - //@ts-ignore - errorHandling.value.map((error) => { - return alert(error); - })} + {errorHandling && ( + + )}