Skip to content

Commit e4957ff

Browse files
authored
feat: handel remove dags (apache#49504)
1 parent 689608d commit e4957ff

File tree

4 files changed

+168
-1
lines changed

4 files changed

+168
-1
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*!
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { Text, Heading, HStack, useDisclosure } from "@chakra-ui/react";
20+
import { FiTrash2 } from "react-icons/fi";
21+
import { useNavigate } from "react-router-dom";
22+
23+
import { Button, Dialog } from "src/components/ui";
24+
import ActionButton from "src/components/ui/ActionButton";
25+
import { useDeleteDag } from "src/queries/useDeleteDag";
26+
27+
type DeleteDagButtonProps = {
28+
readonly dagDisplayName: string;
29+
readonly dagId: string;
30+
readonly withText?: boolean;
31+
};
32+
33+
const DeleteDagButton = ({ dagDisplayName, dagId, withText = true }: DeleteDagButtonProps) => {
34+
const { onClose, onOpen, open } = useDisclosure();
35+
const navigate = useNavigate();
36+
37+
const { isPending, mutate: deleteDag } = useDeleteDag({
38+
dagId,
39+
onSuccessConfirm: () => {
40+
onClose();
41+
navigate("/dags");
42+
},
43+
});
44+
45+
return (
46+
<>
47+
<ActionButton
48+
actionName="Delete DAG"
49+
colorPalette="red"
50+
icon={<FiTrash2 />}
51+
onClick={onOpen}
52+
text="Delete DAG"
53+
variant="solid"
54+
withText={withText}
55+
/>
56+
57+
<Dialog.Root lazyMount onOpenChange={onClose} open={open} size="md" unmountOnExit>
58+
<Dialog.Content backdrop>
59+
<Dialog.Header>
60+
<Heading size="lg">Delete DAG</Heading>
61+
</Dialog.Header>
62+
<Dialog.CloseTrigger />
63+
<Dialog.Body>
64+
<Text>
65+
Are you sure you want to delete <strong>{dagDisplayName}</strong>? This action cannot be undone.
66+
</Text>
67+
<Text color="red.500" fontWeight="bold" mt={4}>
68+
This will remove all metadata related to the DAG, including DAG Runs and Tasks.
69+
</Text>
70+
</Dialog.Body>
71+
<Dialog.Footer>
72+
<HStack justifyContent="flex-end" width="100%">
73+
<Button onClick={onClose} variant="outline">
74+
Cancel
75+
</Button>
76+
<Button colorPalette="red" loading={isPending} onClick={() => deleteDag({ dagId })}>
77+
<FiTrash2 style={{ marginRight: "8px" }} /> Delete
78+
</Button>
79+
</HStack>
80+
</Dialog.Footer>
81+
</Dialog.Content>
82+
</Dialog.Root>
83+
</>
84+
);
85+
};
86+
87+
export default DeleteDagButton;

airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Box, Flex, HStack, SimpleGrid, Link, Spinner } from "@chakra-ui/react";
2020
import { Link as RouterLink } from "react-router-dom";
2121

2222
import type { DAGWithLatestDagRunsResponse } from "openapi/requests/types.gen";
23+
import DeleteDagButton from "src/components/DagActions/DeleteDagButton";
2324
import DagRunInfo from "src/components/DagRunInfo";
2425
import { Stat } from "src/components/Stat";
2526
import { TogglePause } from "src/components/TogglePause";
@@ -52,8 +53,14 @@ export const DagCard = ({ dag }: Props) => {
5253
<DagTags tags={dag.tags} />
5354
</HStack>
5455
<HStack>
55-
<TogglePause dagDisplayName={dag.dag_display_name} dagId={dag.dag_id} isPaused={dag.is_paused} />
56+
<TogglePause
57+
dagDisplayName={dag.dag_display_name}
58+
dagId={dag.dag_id}
59+
isPaused={dag.is_paused}
60+
pr={2}
61+
/>
5662
<TriggerDAGButton dag={dag} withText={false} />
63+
<DeleteDagButton dagDisplayName={dag.dag_display_name} dagId={dag.dag_id} withText={false} />
5764
</HStack>
5865
</Flex>
5966
<SimpleGrid columns={4} gap={1} height={20} px={3}>

airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { Link as RouterLink, useSearchParams } from "react-router-dom";
3131
import { useLocalStorage } from "usehooks-ts";
3232

3333
import type { DagRunState, DAGWithLatestDagRunsResponse } from "openapi/requests/types.gen";
34+
import DeleteDagButton from "src/components/DagActions/DeleteDagButton";
3435
import DagRunInfo from "src/components/DagRunInfo";
3536
import { DataTable } from "src/components/DataTable";
3637
import { ToggleTableDisplay } from "src/components/DataTable/ToggleTableDisplay";
@@ -129,6 +130,18 @@ const columns: Array<ColumnDef<DAGWithLatestDagRunsResponse>> = [
129130
enableSorting: false,
130131
header: "",
131132
},
133+
{
134+
accessorKey: "delete",
135+
cell: ({ row }) => (
136+
<DeleteDagButton
137+
dagDisplayName={row.original.dag_display_name}
138+
dagId={row.original.dag_id}
139+
withText={false}
140+
/>
141+
),
142+
enableSorting: false,
143+
header: "",
144+
},
132145
];
133146

134147
const {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*!
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { useQueryClient } from "@tanstack/react-query";
20+
21+
import { useDagServiceDeleteDag } from "openapi/queries";
22+
import { useDagServiceGetDagKey, useDagServiceGetDagsKey } from "openapi/queries";
23+
import { toaster } from "src/components/ui";
24+
25+
const onError = () => {
26+
toaster.create({
27+
description: "Delete DAG request failed",
28+
title: "Failed to delete DAG",
29+
type: "error",
30+
});
31+
};
32+
33+
export const useDeleteDag = ({
34+
dagId,
35+
onSuccessConfirm,
36+
}: {
37+
dagId: string;
38+
onSuccessConfirm: () => void;
39+
}) => {
40+
const queryClient = useQueryClient();
41+
42+
const onSuccess = async () => {
43+
const queryKeys = [[useDagServiceGetDagsKey], [useDagServiceGetDagKey, { dagId }]];
44+
45+
await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ queryKey: key })));
46+
47+
toaster.create({
48+
description: "The DAG deletion request was successful.",
49+
title: "DAG Deleted Successfully",
50+
type: "success",
51+
});
52+
53+
onSuccessConfirm();
54+
};
55+
56+
return useDagServiceDeleteDag({
57+
onError,
58+
onSuccess,
59+
});
60+
};

0 commit comments

Comments
 (0)