Skip to content

Commit 269b16a

Browse files
bbovenziCloud Composer Team
authored and
Cloud Composer Team
committed
Add filter task upstream/downstream to grid view (#29885)
* Add filter task upstream/downstream to grid view * Update button text, move reset root to dropdown GitOrigin-RevId: 0a91a0bcc26f402fcd3b09c83554d31908b2d847
1 parent be50a7e commit 269b16a

File tree

13 files changed

+309
-96
lines changed

13 files changed

+309
-96
lines changed

airflow/www/static/js/api/useGraphData.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ import axios, { AxiosResponse } from "axios";
2222

2323
import { getMetaValue } from "src/utils";
2424
import type { DepNode } from "src/types";
25+
import useFilters, {
26+
FILTER_DOWNSTREAM_PARAM,
27+
FILTER_UPSTREAM_PARAM,
28+
ROOT_PARAM,
29+
} from "src/dag/useFilters";
2530

2631
const DAG_ID_PARAM = "dag_id";
2732

2833
const dagId = getMetaValue(DAG_ID_PARAM);
2934
const graphDataUrl = getMetaValue("graph_data_url");
30-
const urlRoot = getMetaValue("root");
3135

3236
interface GraphData {
3337
edges: WebserverEdge[];
@@ -40,15 +44,23 @@ export interface WebserverEdge {
4044
targetId: string;
4145
}
4246

43-
const useGraphData = () =>
44-
useQuery("graphData", async () => {
45-
const params = {
46-
[DAG_ID_PARAM]: dagId,
47-
root: urlRoot || undefined,
48-
filter_upstream: true,
49-
filter_downstream: true,
50-
};
51-
return axios.get<AxiosResponse, GraphData>(graphDataUrl, { params });
52-
});
47+
const useGraphData = () => {
48+
const {
49+
filters: { root, filterDownstream, filterUpstream },
50+
} = useFilters();
51+
52+
return useQuery(
53+
["graphData", root, filterUpstream, filterDownstream],
54+
async () => {
55+
const params = {
56+
[DAG_ID_PARAM]: dagId,
57+
[ROOT_PARAM]: root,
58+
[FILTER_UPSTREAM_PARAM]: filterUpstream,
59+
[FILTER_DOWNSTREAM_PARAM]: filterDownstream,
60+
};
61+
return axios.get<AxiosResponse, GraphData>(graphDataUrl, { params });
62+
}
63+
);
64+
};
5365

5466
export default useGraphData;

airflow/www/static/js/api/useGridData.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import useFilters, {
2929
RUN_STATE_PARAM,
3030
RUN_TYPE_PARAM,
3131
now,
32+
FILTER_DOWNSTREAM_PARAM,
33+
FILTER_UPSTREAM_PARAM,
34+
ROOT_PARAM,
3235
} from "src/dag/useFilters";
3336
import type { Task, DagRun, RunOrdering } from "src/types";
3437
import { camelCase } from "lodash";
@@ -38,7 +41,6 @@ const DAG_ID_PARAM = "dag_id";
3841
// dagId comes from dag.html
3942
const dagId = getMetaValue(DAG_ID_PARAM);
4043
const gridDataUrl = getMetaValue("grid_data_url");
41-
const urlRoot = getMetaValue("root");
4244

4345
export interface GridData {
4446
dagRuns: DagRun[];
@@ -68,14 +70,33 @@ const useGridData = () => {
6870
const { isRefreshOn, stopRefresh } = useAutoRefresh();
6971
const errorToast = useErrorToast();
7072
const {
71-
filters: { baseDate, numRuns, runType, runState },
73+
filters: {
74+
baseDate,
75+
numRuns,
76+
runType,
77+
runState,
78+
root,
79+
filterDownstream,
80+
filterUpstream,
81+
},
7282
} = useFilters();
7383

7484
const query = useQuery(
75-
["gridData", baseDate, numRuns, runType, runState],
85+
[
86+
"gridData",
87+
baseDate,
88+
numRuns,
89+
runType,
90+
runState,
91+
root,
92+
filterUpstream,
93+
filterDownstream,
94+
],
7695
async () => {
7796
const params = {
78-
root: urlRoot || undefined,
97+
[ROOT_PARAM]: root,
98+
[FILTER_UPSTREAM_PARAM]: filterUpstream,
99+
[FILTER_DOWNSTREAM_PARAM]: filterDownstream,
79100
[DAG_ID_PARAM]: dagId,
80101
[BASE_DATE_PARAM]: baseDate === now ? undefined : baseDate,
81102
[NUM_RUNS_PARAM]: numRuns,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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+
import React from "react";
21+
import {
22+
Flex,
23+
Button,
24+
Menu,
25+
MenuButton,
26+
MenuItem,
27+
MenuList,
28+
} from "@chakra-ui/react";
29+
import useFilters from "src/dag/useFilters";
30+
import { MdArrowDropDown } from "react-icons/md";
31+
32+
interface Props {
33+
taskId: string;
34+
}
35+
36+
const FilterTasks = ({ taskId }: Props) => {
37+
const {
38+
filters: { root },
39+
onFilterTasksChange,
40+
resetRoot,
41+
} = useFilters();
42+
43+
const onFilterUpstream = () =>
44+
onFilterTasksChange({
45+
root: taskId,
46+
filterUpstream: true,
47+
filterDownstream: false,
48+
});
49+
50+
const onFilterDownstream = () =>
51+
onFilterTasksChange({
52+
root: taskId,
53+
filterUpstream: false,
54+
filterDownstream: true,
55+
});
56+
57+
const onFilterAll = () =>
58+
onFilterTasksChange({
59+
root: taskId,
60+
filterUpstream: true,
61+
filterDownstream: true,
62+
});
63+
64+
const label = "Filter upstream and/or downstream of a task";
65+
66+
return (
67+
<Menu>
68+
<MenuButton
69+
as={Button}
70+
variant="outline"
71+
colorScheme="blue"
72+
transition="all 0.2s"
73+
title={label}
74+
aria-label={label}
75+
>
76+
<Flex>
77+
{!root ? "Filter Tasks " : "Clear Task Filter "}
78+
<MdArrowDropDown size="16px" />
79+
</Flex>
80+
</MenuButton>
81+
<MenuList>
82+
<MenuItem onClick={onFilterUpstream}>Filter Upstream</MenuItem>
83+
<MenuItem onClick={onFilterDownstream}>Filter Downstream</MenuItem>
84+
<MenuItem onClick={onFilterAll}>Filter Upstream & Downstream</MenuItem>
85+
{!!root && <MenuItem onClick={resetRoot}>Reset Root</MenuItem>}
86+
</MenuList>
87+
</Menu>
88+
);
89+
};
90+
91+
export default FilterTasks;

airflow/www/static/js/dag/details/graph/index.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { useOffsetTop } from "src/utils";
3737
import { useGraphLayout } from "src/utils/graph";
3838
import Tooltip from "src/components/Tooltip";
3939
import { useContainerRef } from "src/context/containerRef";
40+
import useFilters from "src/dag/useFilters";
4041

4142
import Edge from "./Edge";
4243
import Node, { CustomNodeProps } from "./Node";
@@ -56,6 +57,10 @@ const Graph = ({ openGroupIds, onToggleGroups }: Props) => {
5657
const { data } = useGraphData();
5758
const [arrange, setArrange] = useState(data?.arrange || "LR");
5859

60+
const {
61+
filters: { root, filterDownstream, filterUpstream },
62+
} = useFilters();
63+
5964
useEffect(() => {
6065
setArrange(data?.arrange || "LR");
6166
}, [data?.arrange]);
@@ -71,9 +76,14 @@ const Graph = ({ openGroupIds, onToggleGroups }: Props) => {
7176
data: { dagRuns, groups },
7277
} = useGridData();
7378
const { colors } = useTheme();
74-
const { setCenter } = useReactFlow();
79+
const { setCenter, setViewport } = useReactFlow();
7580
const latestDagRunId = dagRuns[dagRuns.length - 1]?.runId;
7681

82+
// Reset viewport when tasks are filtered
83+
useEffect(() => {
84+
setViewport({ x: 0, y: 0, zoom: 1 });
85+
}, [root, filterDownstream, filterUpstream, setViewport]);
86+
7787
const offsetTop = useOffsetTop(graphRef);
7888

7989
let nodes: ReactFlowNode<CustomNodeProps>[] = [];

airflow/www/static/js/dag/details/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import Graph from "./graph";
4545
import MappedInstances from "./taskInstance/MappedInstances";
4646
import Logs from "./taskInstance/Logs";
4747
import BackToTaskSummary from "./taskInstance/BackToTaskSummary";
48+
import FilterTasks from "./FilterTasks";
4849

4950
const dagId = getMetaValue("dag_id")!;
5051

@@ -145,7 +146,10 @@ const Details = ({ openGroupIds, onToggleGroups }: Props) => {
145146

146147
return (
147148
<Flex flexDirection="column" pl={3} height="100%">
148-
<Header />
149+
<Flex alignItems="center" justifyContent="space-between">
150+
<Header />
151+
<Flex>{taskId && runId && <FilterTasks taskId={taskId} />}</Flex>
152+
</Flex>
149153
<Divider my={2} />
150154
<Tabs
151155
size="lg"

airflow/www/static/js/dag/details/taskInstance/Nav.tsx

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,19 @@ import { Flex } from "@chakra-ui/react";
2222

2323
import { getMetaValue, appendSearchParams } from "src/utils";
2424
import LinkButton from "src/components/LinkButton";
25-
import type { Task, DagRun } from "src/types";
25+
import type { Task } from "src/types";
2626
import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
2727

2828
const dagId = getMetaValue("dag_id");
2929
const isK8sExecutor = getMetaValue("k8s_or_k8scelery_executor") === "True";
30-
const numRuns = getMetaValue("num_runs");
31-
const baseDate = getMetaValue("base_date");
3230
const taskInstancesUrl = getMetaValue("task_instances_list_url");
3331
const renderedK8sUrl = getMetaValue("rendered_k8s_url");
3432
const renderedTemplatesUrl = getMetaValue("rendered_templates_url");
3533
const xcomUrl = getMetaValue("xcom_url");
3634
const taskUrl = getMetaValue("task_url");
3735
const gridUrl = getMetaValue("grid_url");
38-
const gridUrlNoRoot = getMetaValue("grid_url_no_root");
3936

4037
interface Props {
41-
runId: DagRun["runId"];
4238
taskId: Task["id"];
4339
executionDate: string;
4440
operator?: string;
@@ -47,10 +43,7 @@ interface Props {
4743
}
4844

4945
const Nav = forwardRef<HTMLDivElement, Props>(
50-
(
51-
{ runId, taskId, executionDate, operator, isMapped = false, mapIndex },
52-
ref
53-
) => {
46+
({ taskId, executionDate, operator, isMapped = false, mapIndex }, ref) => {
5447
if (!taskId) return null;
5548
const params = new URLSearchParamsWrapper({
5649
task_id: taskId,
@@ -70,23 +63,11 @@ const Nav = forwardRef<HTMLDivElement, Props>(
7063
execution_date: executionDate,
7164
}).toString();
7265

73-
const filterParams = new URLSearchParamsWrapper({
74-
task_id: taskId,
75-
dag_run_id: runId,
76-
root: taskId,
77-
});
78-
7966
if (mapIndex !== undefined && mapIndex >= 0)
8067
listParams.append("_flt_0_map_index", mapIndex.toString());
81-
if (baseDate) filterParams.append("base_date", baseDate);
82-
if (numRuns) filterParams.append("num_runs", numRuns);
8368

8469
const allInstancesLink = `${taskInstancesUrl}?${listParams.toString()}`;
8570

86-
const filterUpstreamLink = appendSearchParams(
87-
gridUrlNoRoot,
88-
filterParams.toString()
89-
);
9071
const subDagLink = appendSearchParams(
9172
gridUrl.replace(dagId, `${dagId}.${taskId}`),
9273
subDagParams
@@ -116,7 +97,6 @@ const Nav = forwardRef<HTMLDivElement, Props>(
11697
>
11798
List Instances, all runs
11899
</LinkButton>
119-
<LinkButton href={filterUpstreamLink}>Filter Upstream</LinkButton>
120100
</Flex>
121101
);
122102
}

airflow/www/static/js/dag/details/taskInstance/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ const TaskInstance = ({ taskId, runId, mapIndex }: Props) => {
9292
{!isGroup && (
9393
<TaskNav
9494
taskId={taskId}
95-
runId={runId}
9695
isMapped={isMapped}
9796
mapIndex={mapIndex}
9897
executionDate={executionDate}

airflow/www/static/js/dag/grid/ResetRoot.tsx

Lines changed: 0 additions & 42 deletions
This file was deleted.

airflow/www/static/js/dag/nav/FilterBar.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ import AutoRefresh from "src/components/AutoRefresh";
2626

2727
import { useTimezone } from "src/context/timezone";
2828
import { isoFormatWithoutTZ } from "src/datetime_utils";
29-
import useFilters from "../useFilters";
30-
import ResetRoot from "../grid/ResetRoot";
29+
import useFilters from "src/dag/useFilters";
3130

3231
declare const filtersOptions: {
3332
dagStates: RunState[];
@@ -123,10 +122,7 @@ const FilterBar = () => {
123122
</Button>
124123
</Box>
125124
</Flex>
126-
<Flex>
127-
<AutoRefresh />
128-
<ResetRoot />
129-
</Flex>
125+
<AutoRefresh />
130126
</Flex>
131127
);
132128
};

0 commit comments

Comments
 (0)