diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstanceButton.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstanceButton.tsx new file mode 100644 index 0000000000000..f941db3de2fcd --- /dev/null +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstanceButton.tsx @@ -0,0 +1,77 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); 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. + */ +import { useDisclosure } from "@chakra-ui/react"; +import { FiTrash2 } from "react-icons/fi"; + +import type { TaskInstanceResponse } from "openapi/requests/types.gen"; +import DeleteDialog from "src/components/DeleteDialog"; +import ActionButton from "src/components/ui/ActionButton"; +import { useDeleteTaskInstance } from "src/queries/useDeleteTaskInstance"; + +type DeleteTaskInstanceButtonProps = { + readonly taskInstance: TaskInstanceResponse; + readonly withText?: boolean; +}; + +const DeleteTaskInstanceButton = ({ taskInstance, withText = true }: DeleteTaskInstanceButtonProps) => { + const { onClose, onOpen, open } = useDisclosure(); + + const { isPending, mutate: deleteTaskInstance } = useDeleteTaskInstance({ + dagId: taskInstance.dag_id, + dagRunId: taskInstance.dag_run_id, + mapIndex: taskInstance.map_index, + onSuccessConfirm: () => { + onClose(); + }, + taskId: taskInstance.task_id, + }); + + return ( + <> + } + onClick={onOpen} + text="Delete Task Instance" + variant="solid" + withText={withText} + /> + + { + deleteTaskInstance({ + dagId: taskInstance.dag_id, + dagRunId: taskInstance.dag_run_id, + mapIndex: taskInstance.map_index, + taskId: taskInstance.task_id, + }); + }} + open={open} + resourceName={taskInstance.task_id} + title="Delete Task Instance" + warningText="This will remove all metadata related to the Task Instance." + /> + + ); +}; + +export default DeleteTaskInstanceButton; diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx index 54e3139c5736a..74937073e493b 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx @@ -36,6 +36,7 @@ import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searc import { getDuration, useAutoRefresh, isStatePending } from "src/utils"; import { getTaskInstanceLink } from "src/utils/links"; +import DeleteTaskInstanceButton from "./DeleteTaskInstanceButton"; import { TaskInstancesFilter } from "./TaskInstancesFilter"; type TaskInstanceRow = { row: { original: TaskInstanceResponse } }; @@ -155,6 +156,7 @@ const taskInstanceColumns = ( + ), enableSorting: false, diff --git a/airflow-core/src/airflow/ui/src/queries/useDeleteTaskInstance.ts b/airflow-core/src/airflow/ui/src/queries/useDeleteTaskInstance.ts new file mode 100644 index 0000000000000..e299b208e1a8f --- /dev/null +++ b/airflow-core/src/airflow/ui/src/queries/useDeleteTaskInstance.ts @@ -0,0 +1,80 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); 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. + */ +import { useQueryClient } from "@tanstack/react-query"; + +import { + useTaskInstanceServiceDeleteTaskInstance, + useTaskInstanceServiceGetTaskInstanceKey, + useTaskInstanceServiceGetTaskInstancesKey, + UseGridServiceGridDataKeyFn, + useDagRunServiceGetDagRunsKey, + UseDagRunServiceGetDagRunKeyFn, +} from "openapi/queries"; +import { toaster } from "src/components/ui"; + +type DeleteTaskInstanceParams = { + dagId: string; + dagRunId: string; + mapIndex?: number; + onSuccessConfirm: () => void; + taskId: string; +}; + +const onError = () => { + toaster.create({ + description: "Delete Task Instance request failed.", + title: "Failed to delete Task Instance", + type: "error", + }); +}; + +export const useDeleteTaskInstance = ({ + dagId, + dagRunId, + mapIndex, + onSuccessConfirm, + taskId, +}: DeleteTaskInstanceParams) => { + const queryClient = useQueryClient(); + + const onSuccess = async () => { + const queryKeys = [ + UseDagRunServiceGetDagRunKeyFn({ dagId, dagRunId }), + [useDagRunServiceGetDagRunsKey], + [useTaskInstanceServiceGetTaskInstancesKey], + [useTaskInstanceServiceGetTaskInstanceKey, { dagId, dagRunId, mapIndex, taskId }], + UseGridServiceGridDataKeyFn({ dagId }, [{ dagId }]), + ]; + + await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ queryKey: key }))); + + toaster.create({ + description: "The Task Instance deletion request was successful.", + title: "Task Instance Deleted Successfully", + type: "success", + }); + + onSuccessConfirm(); + }; + + return useTaskInstanceServiceDeleteTaskInstance({ + onError, + onSuccess, + }); +};