From 8ce3163848cd05a862933d9fb52b887870e5652b Mon Sep 17 00:00:00 2001 From: Ian Bolton Date: Thu, 17 Aug 2023 13:02:14 +0000 Subject: [PATCH] :bug: Add taskgroup when uploading binary (#1280) - Improve handling of taskgroup state by using contextAPI - Async create the taskgroup before uploading the custom binary in the single app use case. This is currently broken because we require a task group to exist before navigating to the upload file screen. This regression was caused by the move away from useEffect here https://github.com/konveyor/tackle2-ui/pull/1267 Resolves https://issues.redhat.com/browse/MTA-1175 Signed-off-by: ibolton336 --- .../analysis-wizard/analysis-wizard.tsx | 45 ++++---------- .../components/TaskGroupContext.tsx | 34 ++++++++++ .../components/upload-binary.tsx | 62 ++++++++++++++----- .../analysis-wizard/custom-rules.tsx | 10 +-- .../applications/analysis-wizard/set-mode.tsx | 11 +--- .../applications-table-analyze.tsx | 17 ++--- 6 files changed, 111 insertions(+), 68 deletions(-) create mode 100644 client/src/app/pages/applications/analysis-wizard/components/TaskGroupContext.tsx diff --git a/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx b/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx index e57226f6df..1d7f055f10 100644 --- a/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx +++ b/client/src/app/pages/applications/analysis-wizard/analysis-wizard.tsx @@ -10,7 +10,6 @@ import { useTranslation } from "react-i18next"; import { Application, - IReadFile, TaskData, Taskgroup, TaskgroupTask, @@ -36,9 +35,8 @@ import { } from "./schema"; import { useAsyncYupValidation } from "@app/hooks/useAsyncYupValidation"; import { CustomRules } from "./custom-rules"; -import { useSetting } from "@app/queries/settings"; -import defaultSources from "./sources"; import { useFetchIdentities } from "@app/queries/identities"; +import { TaskGroupProvider, useTaskGroup } from "./components/TaskGroupContext"; interface IAnalysisWizard { applications: Application[]; @@ -67,7 +65,7 @@ const defaultTaskData: TaskData = { }, }; -const defaultTaskgroup: Taskgroup = { +export const defaultTaskgroup: Taskgroup = { name: `taskgroup.analyzer`, addon: "analyzer", data: { @@ -96,18 +94,16 @@ export const AnalysisWizard: React.FC = ({ const { pushNotification } = React.useContext(NotificationsContext); - const [currentTaskgroup, setCurrentTaskgroup] = - React.useState(); + const { taskGroup, updateTaskGroup } = useTaskGroup(); const [stepIdReached, setStepIdReached] = React.useState(1); const isMutating = useIsMutating(); const onCreateTaskgroupSuccess = (data: Taskgroup) => { - setCurrentTaskgroup(data); + updateTaskGroup(data); }; const onCreateTaskgroupError = (error: Error | unknown) => { - console.log("Taskgroup creation failed: ", error); pushNotification({ title: "Taskgroup creation failed", variant: "danger", @@ -139,11 +135,10 @@ export const AnalysisWizard: React.FC = ({ ); const onDeleteTaskgroupSuccess = () => { - setCurrentTaskgroup(null); + updateTaskGroup(null); }; const onDeleteTaskgroupError = (error: Error | unknown) => { - console.log("Taskgroup: delete failed: ", error); pushNotification({ title: "Taskgroup: delete failed", variant: "danger", @@ -282,20 +277,20 @@ export const AnalysisWizard: React.FC = ({ const isModeValid = applications.every((app) => isModeSupported(app, mode)); const handleCancel = () => { - if (currentTaskgroup && currentTaskgroup.id) { - deleteTaskgroup(currentTaskgroup.id); + if (taskGroup && taskGroup.id) { + deleteTaskgroup(taskGroup.id); } - setCurrentTaskgroup(null); + updateTaskGroup(null); reset(); onClose(); }; const onSubmit = (fieldValues: AnalysisWizardFormValues) => { - if (currentTaskgroup) { - const taskgroup = setupTaskgroup(currentTaskgroup, fieldValues); + if (taskGroup) { + const taskgroup = setupTaskgroup(taskGroup, fieldValues); submitTaskgroup(taskgroup); } - setCurrentTaskgroup(null); + updateTaskGroup(null); reset(); onClose(); }; @@ -306,7 +301,7 @@ export const AnalysisWizard: React.FC = ({ ) => { if (id && stepIdReached < (id as number)) setStepIdReached(id as number); if (id === StepId.SetTargets) { - if (!currentTaskgroup) { + if (!taskGroup) { createTaskgroup(defaultTaskgroup); } } @@ -335,11 +330,6 @@ export const AnalysisWizard: React.FC = ({ component: ( ), @@ -365,15 +355,7 @@ export const AnalysisWizard: React.FC = ({ { id: StepId.CustomRules, name: t("wizard.terms.customRules"), - component: ( - - ), + component: , ...getStepNavProps(StepId.CustomRules), }, { @@ -392,7 +374,6 @@ export const AnalysisWizard: React.FC = ({ ...getStepNavProps(StepId.Review), }, ]; - return ( <> {isOpen && ( diff --git a/client/src/app/pages/applications/analysis-wizard/components/TaskGroupContext.tsx b/client/src/app/pages/applications/analysis-wizard/components/TaskGroupContext.tsx new file mode 100644 index 0000000000..5781b3c34a --- /dev/null +++ b/client/src/app/pages/applications/analysis-wizard/components/TaskGroupContext.tsx @@ -0,0 +1,34 @@ +import { Taskgroup } from "@app/api/models"; +import React, { createContext, useContext, useState } from "react"; + +interface TaskGroupContext { + updateTaskGroup: (taskGroup: Taskgroup | null) => void; + taskGroup: Taskgroup | null; +} + +const TaskGroupContext = createContext({ + updateTaskGroup: () => {}, + taskGroup: null, +}); + +export const useTaskGroup = () => useContext(TaskGroupContext); + +interface TaskGroupProvider { + children: React.ReactNode; +} + +export const TaskGroupProvider: React.FunctionComponent = ({ + children, +}) => { + const [taskGroup, setTaskGroup] = useState(null); + + const updateTaskGroup = (newTaskGroup: Taskgroup | null) => { + setTaskGroup(newTaskGroup); + }; + + return ( + + {children} + + ); +}; diff --git a/client/src/app/pages/applications/analysis-wizard/components/upload-binary.tsx b/client/src/app/pages/applications/analysis-wizard/components/upload-binary.tsx index f64bed5771..48148a394c 100644 --- a/client/src/app/pages/applications/analysis-wizard/components/upload-binary.tsx +++ b/client/src/app/pages/applications/analysis-wizard/components/upload-binary.tsx @@ -10,6 +10,7 @@ import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon"; import { useFormContext } from "react-hook-form"; import { + useCreateTaskgroupMutation, useRemoveUploadedFileMutation, useUploadFileTaskgroupMutation, } from "@app/queries/taskgroups"; @@ -19,12 +20,12 @@ import spacing from "@patternfly/react-styles/css/utilities/Spacing/spacing"; import { uploadLimit } from "@app/Constants"; import { NotificationsContext } from "@app/components/NotificationsContext"; import { AnalysisWizardFormValues } from "../schema"; +import { useTaskGroup } from "./TaskGroupContext"; +import { Taskgroup } from "@app/api/models"; +import { defaultTaskgroup } from "../analysis-wizard"; -interface IUploadBinary { - taskgroupID: number; -} - -export const UploadBinary: React.FC = ({ taskgroupID }) => { +export const UploadBinary: React.FC = () => { + const { taskGroup, updateTaskGroup } = useTaskGroup(); const { setValue, watch } = useFormContext(); const artifact = watch("artifact"); @@ -90,6 +91,22 @@ export const UploadBinary: React.FC = ({ taskgroupID }) => { completedRemove, failedRemove ); + const onCreateTaskgroupSuccess = (data: Taskgroup) => { + updateTaskGroup(data); + }; + + const onCreateTaskgroupError = (error: Error | unknown) => { + console.log("Taskgroup creation failed: ", error); + pushNotification({ + title: "Taskgroup creation failed", + variant: "danger", + }); + }; + + const { mutateAsync: createTaskgroup } = useCreateTaskgroupMutation( + onCreateTaskgroupSuccess, + onCreateTaskgroupError + ); const handleFileDrop = (_: DropEvent, droppedFiles: File[]) => { if (droppedFiles[0]) { @@ -98,12 +115,26 @@ export const UploadBinary: React.FC = ({ taskgroupID }) => { setFileUploadStatus(undefined); const form = new FormData(); form.append("file", droppedFiles[0]); - uploadFile({ - id: taskgroupID, - path: `binary/${droppedFiles[0].name}`, - formData: form, - file: droppedFiles[0], - }); + if (!taskGroup) { + createTaskgroup(defaultTaskgroup).then((data) => { + updateTaskGroup(data); + data.id && + uploadFile({ + id: data?.id, + path: `binary/${droppedFiles[0].name}`, + formData: form, + file: droppedFiles[0], + }); + }); + } else { + taskGroup.id && + uploadFile({ + id: taskGroup?.id, + path: `binary/${droppedFiles[0].name}`, + formData: form, + file: droppedFiles[0], + }); + } setValue("artifact", droppedFiles[0]); } }; @@ -175,10 +206,11 @@ export const UploadBinary: React.FC = ({ taskgroupID }) => { key={artifact.name} customFileHandler={handleFile} onClearClick={() => { - removeFile({ - id: taskgroupID, - path: `binary/${artifact}`, - }); + taskGroup?.id && + removeFile({ + id: taskGroup?.id, + path: `binary/${artifact}`, + }); setValue("artifact", null); }} progressAriaLabel={"text"} diff --git a/client/src/app/pages/applications/analysis-wizard/custom-rules.tsx b/client/src/app/pages/applications/analysis-wizard/custom-rules.tsx index f8f33dbf33..5fb665f447 100644 --- a/client/src/app/pages/applications/analysis-wizard/custom-rules.tsx +++ b/client/src/app/pages/applications/analysis-wizard/custom-rules.tsx @@ -54,11 +54,11 @@ import { OptionWithValue, SimpleSelect } from "@app/components/SimpleSelect"; import { toOptionLike } from "@app/utils/model-utils"; import { useFetchIdentities } from "@app/queries/identities"; import useRuleFiles from "@app/hooks/useRuleFiles"; -interface CustomRulesProps { - taskgroupID: number | null; -} -export const CustomRules: React.FC = (props) => { +import { useTaskGroup } from "./components/TaskGroupContext"; + +export const CustomRules: React.FC = () => { const { t } = useTranslation(); + const { taskGroup, updateTaskGroup } = useTaskGroup(); const { watch, setValue, control, getValues } = useFormContext(); @@ -92,7 +92,7 @@ export const CustomRules: React.FC = (props) => { successfullyReadFileCount, handleFile, removeFiles, - } = useRuleFiles(props?.taskgroupID, values.customRulesFiles); + } = useRuleFiles(taskGroup?.id, values.customRulesFiles); const repositoryTypeOptions: OptionWithValue[] = [ { diff --git a/client/src/app/pages/applications/analysis-wizard/set-mode.tsx b/client/src/app/pages/applications/analysis-wizard/set-mode.tsx index e5edea1c9e..6a2ce9238c 100644 --- a/client/src/app/pages/applications/analysis-wizard/set-mode.tsx +++ b/client/src/app/pages/applications/analysis-wizard/set-mode.tsx @@ -11,15 +11,10 @@ import { HookFormPFGroupController } from "@app/components/HookFormPFFields"; interface ISetMode { isSingleApp: boolean; - taskgroupID: number | null; isModeValid: boolean; } -export const SetMode: React.FC = ({ - isSingleApp, - taskgroupID, - isModeValid, -}) => { +export const SetMode: React.FC = ({ isSingleApp, isModeValid }) => { const { t } = useTranslation(); const { watch, control, setValue } = @@ -89,9 +84,7 @@ export const SetMode: React.FC = ({

{t("wizard.label.notAllAnalyzableDetails")}

)} - {mode === "binary-upload" && taskgroupID && ( - - )} + {mode === "binary-upload" && } ); }; diff --git a/client/src/app/pages/applications/applications-table-analyze/applications-table-analyze.tsx b/client/src/app/pages/applications/applications-table-analyze/applications-table-analyze.tsx index 513f0a9627..a8f5f1e37d 100644 --- a/client/src/app/pages/applications/applications-table-analyze/applications-table-analyze.tsx +++ b/client/src/app/pages/applications/applications-table-analyze/applications-table-analyze.tsx @@ -70,6 +70,7 @@ import { AppTableWithControls } from "@app/components/AppTableWithControls"; import { ToolbarBulkSelector } from "@app/components/ToolbarBulkSelector"; import { KebabDropdown } from "@app/components/KebabDropdown"; import { NoDataEmptyState } from "@app/components/NoDataEmptyState"; +import { TaskGroupProvider } from "../analysis-wizard/components/TaskGroupContext"; const ENTITY_FIELD = "entity"; @@ -611,13 +612,15 @@ export const ApplicationsTableAnalyze: React.FC = () => { onClose={() => setSaveApplicationsModalState(null)} /> {" "} - { - setAnalyzeModalOpen(false); - }} - /> + + { + setAnalyzeModalOpen(false); + }} + /> +