Skip to content

Commit 2ff19a5

Browse files
authored
Implement backfill banner (apache#47411)
* WIP: Implement backfill banner resolves: apache#43968 * Removed generic Banner in favor of BackfillBanner * Removed usage of useEffect * Added pause/unpause and stop backfill in the banner * Update backfill filter and some names * Removed visibility button * Resolved review comments
1 parent 0495aa8 commit 2ff19a5

File tree

4 files changed

+139
-2
lines changed

4 files changed

+139
-2
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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 { Box, HStack, Spacer, Text } from "@chakra-ui/react";
20+
import { MdPause, MdPlayArrow, MdStop } from "react-icons/md";
21+
22+
import {
23+
useBackfillServiceCancelBackfill,
24+
useBackfillServiceListBackfills,
25+
useBackfillServiceListBackfillsKey,
26+
useBackfillServicePauseBackfill,
27+
useBackfillServiceUnpauseBackfill,
28+
} from "openapi/queries";
29+
import { queryClient } from "src/queryClient";
30+
31+
import Time from "../Time";
32+
import { Button, ProgressBar } from "../ui";
33+
34+
type Props = {
35+
readonly dagId: string;
36+
};
37+
38+
const onSuccess = async () => {
39+
await queryClient.invalidateQueries({
40+
queryKey: [useBackfillServiceListBackfillsKey],
41+
});
42+
};
43+
44+
const BackfillBanner = ({ dagId }: Props) => {
45+
const { data, isLoading } = useBackfillServiceListBackfills({
46+
dagId,
47+
});
48+
const [backfill] = data?.backfills.filter((bf) => bf.completed_at === null) ?? [];
49+
50+
const { isPending: isPausePending, mutate: pauseMutate } = useBackfillServicePauseBackfill({ onSuccess });
51+
const { isPending: isUnPausePending, mutate: unpauseMutate } = useBackfillServiceUnpauseBackfill({
52+
onSuccess,
53+
});
54+
55+
const { isPending: isStopPending, mutate: stopPending } = useBackfillServiceCancelBackfill({ onSuccess });
56+
57+
const togglePause = () => {
58+
if (backfill?.is_paused) {
59+
unpauseMutate({ backfillId: backfill.id });
60+
} else {
61+
pauseMutate({ backfillId: backfill?.id });
62+
}
63+
};
64+
65+
const cancel = () => {
66+
stopPending({ backfillId: backfill?.id });
67+
};
68+
69+
if (isLoading || backfill === undefined) {
70+
return undefined;
71+
}
72+
73+
return (
74+
<Box bg="blue.solid" color="white" fontSize="m" mr="0.5" my="1" px="2" py="1" rounded="lg">
75+
<HStack>
76+
<Text key="backfill">Backfill in progress:</Text>
77+
<>
78+
<Time datetime={data?.backfills[0]?.from_date} /> - <Time datetime={data?.backfills[0]?.to_date} />
79+
</>
80+
<Spacer flex="max-content" />
81+
<ProgressBar size="xs" visibility="visible" />
82+
<Button
83+
aria-label={backfill.is_paused ? "Unpause backfill" : "Pause backfill"}
84+
loading={isPausePending || isUnPausePending}
85+
onClick={() => {
86+
togglePause();
87+
}}
88+
rounded="full"
89+
size="xs"
90+
variant="outline"
91+
>
92+
{backfill.is_paused ? <MdPlayArrow color="white" size="1" /> : <MdPause color="white" size="1" />}
93+
</Button>
94+
<Button
95+
aria-label="Cancel backfill"
96+
loading={isStopPending}
97+
onClick={() => {
98+
cancel();
99+
}}
100+
rounded="full"
101+
size="xs"
102+
variant="outline"
103+
>
104+
<MdStop color="white" size="1" />
105+
</Button>
106+
</HStack>
107+
</Box>
108+
);
109+
};
110+
111+
export default BackfillBanner;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
20+
export { default as BackfillBanner } from "./BackfillBanner";

airflow/ui/src/layouts/Details/DetailsLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useLocalStorage } from "usehooks-ts";
2525

2626
import { useDagServiceGetDag } from "openapi/queries";
2727
import type { DAGResponse } from "openapi/requests/types.gen";
28+
import BackfillBanner from "src/components/Banner/BackfillBanner";
2829
import { ErrorAlert } from "src/components/ErrorAlert";
2930
import { SearchDagsButton } from "src/components/SearchDags";
3031
import TriggerDAGButton from "src/components/TriggerDag/TriggerDAGButton";
@@ -64,6 +65,7 @@ export const DetailsLayout = ({ children, error, isLoading, tabs }: Props) => {
6465
</Flex>
6566
</HStack>
6667
<Toaster />
68+
<BackfillBanner dagId={dagId} />
6769
<Box flex={1} minH={0}>
6870
<PanelGroup autoSaveId={dagId} direction="horizontal">
6971
<Panel defaultSize={dagView === "graph" ? 70 : 20} minSize={6}>

airflow/ui/src/queries/useCreateBackfill.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,19 @@
1818
*/
1919
import { useState } from "react";
2020

21-
import { useBackfillServiceCreateBackfill } from "openapi/queries";
21+
import { useBackfillServiceCreateBackfill, useBackfillServiceListBackfillsKey } from "openapi/queries";
2222
import type { CreateBackfillData } from "openapi/requests/types.gen";
2323
import { toaster } from "src/components/ui";
24+
import { queryClient } from "src/queryClient";
2425

2526
export const useCreateBackfill = ({ onSuccessConfirm }: { onSuccessConfirm: () => void }) => {
2627
const [dateValidationError, setDateValidationError] = useState<unknown>(undefined);
2728
const [error, setError] = useState<unknown>(undefined);
2829

29-
const onSuccess = () => {
30+
const onSuccess = async () => {
31+
await queryClient.invalidateQueries({
32+
queryKey: [useBackfillServiceListBackfillsKey],
33+
});
3034
toaster.create({
3135
description: "Backfill jobs have been successfully triggered.",
3236
title: "Backfill generated",

0 commit comments

Comments
 (0)