Skip to content

Commit

Permalink
Add Task Details page (apache#44828)
Browse files Browse the repository at this point in the history
* Add Task Details page

* Fix sorts and links
  • Loading branch information
bbovenzi authored Dec 11, 2024
1 parent 9aa2cea commit 410b930
Show file tree
Hide file tree
Showing 10 changed files with 408 additions and 48 deletions.
41 changes: 41 additions & 0 deletions airflow/ui/src/assets/TaskIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*!
* 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 { createIcon } from "@chakra-ui/react";

export const TaskIcon = createIcon({
defaultProps: {
height: "1em",
width: "1em",
},
displayName: "Task Icon",
path: (
<g
clip-path="url(#a)"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="m7.99967 10.6666c1.47276 0 2.66663-1.19392 2.66663-2.66668s-1.19387-2.66667-2.66663-2.66667c-1.47275 0-2.66666 1.19391-2.66666 2.66667s1.19391 2.66668 2.66666 2.66668z" />
<path d="m.700195 8h3.966665" />
<path d="m11.3398 8h3.9667" />
</g>
),
viewBox: "0 0 16 16",
});
47 changes: 25 additions & 22 deletions airflow/ui/src/pages/Dag/Tasks/TaskCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
Heading,
VStack,
HStack,
Box,
SimpleGrid,
Text,
} from "@chakra-ui/react";
import { Heading, VStack, Box, SimpleGrid, Text, Link } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";

import type {
TaskResponse,
Expand All @@ -36,22 +30,27 @@ import { Status } from "src/components/ui";
import { TaskRecentRuns } from "./TaskRecentRuns.tsx";

type Props = {
readonly dagId: string;
readonly task: TaskResponse;
readonly taskInstances: Array<TaskInstanceResponse>;
};

export const TaskCard = ({ task, taskInstances }: Props) => (
export const TaskCard = ({ dagId, task, taskInstances }: Props) => (
<Box
borderColor="border.emphasized"
borderRadius={8}
borderWidth={1}
overflow="hidden"
px={3}
py={2}
>
<Text bg="bg.info" color="fg.info" fontWeight="bold" p={2}>
{task.task_display_name ?? task.task_id}
{task.is_mapped ? "[]" : undefined}
</Text>
<SimpleGrid columns={4} gap={4} height={20} px={3} py={2}>
<Link asChild color="fg.info" fontWeight="bold">
<RouterLink to={`/dags/${dagId}/tasks/${task.task_id}`}>
{task.task_display_name ?? task.task_id}
{task.is_mapped ? "[]" : undefined}
</RouterLink>
</Link>
<SimpleGrid columns={4} gap={4} height={20}>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Operator
Expand All @@ -70,14 +69,18 @@ export const TaskCard = ({ task, taskInstances }: Props) => (
</Heading>
{taskInstances[0] ? (
<TaskInstanceTooltip taskInstance={taskInstances[0]}>
<HStack fontSize="sm">
<Time datetime={taskInstances[0].start_date} />
{taskInstances[0].state === null ? undefined : (
<Status state={taskInstances[0].state}>
{taskInstances[0].state}
</Status>
)}
</HStack>
<Link asChild color="fg.info" fontSize="sm">
<RouterLink
to={`/dags/${dagId}/runs/${taskInstances[0].dag_run_id}/tasks/${task.task_id}`}
>
<Time datetime={taskInstances[0].start_date} />
{taskInstances[0].state === null ? undefined : (
<Status state={taskInstances[0].state}>
{taskInstances[0].state}
</Status>
)}
</RouterLink>
</Link>
</TaskInstanceTooltip>
) : undefined}
</VStack>
Expand Down
15 changes: 10 additions & 5 deletions airflow/ui/src/pages/Dag/Tasks/Tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ import { pluralize } from "src/utils";
import { TaskCard } from "./TaskCard";

const cardDef = (
dagId: string,
taskInstances?: Array<TaskInstanceResponse>,
): CardDef<TaskResponse> => ({
card: ({ row }) => (
<TaskCard
dagId={dagId}
task={row}
taskInstances={
taskInstances
Expand All @@ -57,19 +59,19 @@ const cardDef = (
});

export const Tasks = () => {
const { dagId } = useParams();
const { dagId = "" } = useParams();
const {
data,
error: tasksError,
isFetching,
isLoading,
} = useTaskServiceGetTasks({
dagId: dagId ?? "",
dagId,
});

// TODO: Replace dagIdPattern with dagId once supported for better matching
const { data: runsData } = useDagsServiceRecentDagRuns(
{ dagIdPattern: dagId ?? "", dagRunsLimit: 14 },
{ dagIdPattern: dagId, dagRunsLimit: 14 },
undefined,
{
enabled: Boolean(dagId),
Expand All @@ -85,7 +87,7 @@ export const Tasks = () => {
const { data: taskInstancesResponse } =
useTaskInstanceServiceGetTaskInstances(
{
dagId: dagId ?? "",
dagId,
dagRunId: "~",
logicalDateGte: runs.at(-1)?.logical_date ?? "",
},
Expand All @@ -100,7 +102,10 @@ export const Tasks = () => {
{pluralize("Task", data ? data.total_entries : 0)}
</Heading>
<DataTable
cardDef={cardDef(taskInstancesResponse?.task_instances.reverse())}
cardDef={cardDef(
dagId,
taskInstancesResponse?.task_instances.reverse(),
)}
columns={[]}
data={data ? data.tasks : []}
displayMode="card"
Expand Down
20 changes: 13 additions & 7 deletions airflow/ui/src/pages/DagsList/DagCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,19 @@ export const DagCard = ({ dag }: Props) => {
</Stat>
<Stat label="Latest Run">
{latestRun ? (
<DagRunInfo
dataIntervalEnd={latestRun.data_interval_end}
dataIntervalStart={latestRun.data_interval_start}
endDate={latestRun.end_date}
startDate={latestRun.start_date}
state={latestRun.state}
/>
<Link asChild color="fg.info" fontSize="sm">
<RouterLink
to={`/dags/${latestRun.dag_id}/runs/${latestRun.dag_run_id}`}
>
<DagRunInfo
dataIntervalEnd={latestRun.data_interval_end}
dataIntervalStart={latestRun.data_interval_start}
endDate={latestRun.end_date}
startDate={latestRun.start_date}
state={latestRun.state}
/>
</RouterLink>
</Link>
) : undefined}
</Stat>
<Stat label="Next Run">
Expand Down
31 changes: 17 additions & 14 deletions airflow/ui/src/pages/Run/TaskInstances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,8 @@ const columns: Array<ColumnDef<TaskInstanceResponse>> = [
</RouterLink>
</Link>
),
header: "Task ID",
},
{
accessorKey: "map_index",
header: "Map Index",
},
{
accessorKey: "try_number",
enableSorting: false,
header: "Try Number",
header: "Task ID",
},
{
accessorKey: "state",
Expand All @@ -61,11 +53,6 @@ const columns: Array<ColumnDef<TaskInstanceResponse>> = [
}) => <Status state={state}>{state}</Status>,
header: () => "State",
},
{
accessorKey: "operator",
enableSorting: false,
header: "Operator",
},
{
accessorKey: "start_date",
cell: ({ row: { original } }) => <Time datetime={original.start_date} />,
Expand All @@ -76,6 +63,22 @@ const columns: Array<ColumnDef<TaskInstanceResponse>> = [
cell: ({ row: { original } }) => <Time datetime={original.end_date} />,
header: "End Date",
},
{
accessorKey: "map_index",
header: "Map Index",
},

{
accessorKey: "try_number",
enableSorting: false,
header: "Try Number",
},
{
accessorKey: "operator",
enableSorting: false,
header: "Operator",
},

{
cell: ({ row: { original } }) =>
`${dayjs.duration(dayjs(original.end_date).diff(original.start_date)).asSeconds().toFixed(2)}s`,
Expand Down
53 changes: 53 additions & 0 deletions airflow/ui/src/pages/Task/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*!
* 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 { Box, Flex, Heading, HStack, SimpleGrid, Text } from "@chakra-ui/react";

import type { TaskResponse } from "openapi/requests/types.gen";
import { TaskIcon } from "src/assets/TaskIcon";
import DagDocumentation from "src/components/DagDocumentation";
import { Stat } from "src/components/Stat";

export const Header = ({ task }: { readonly task: TaskResponse }) => (
<Box borderColor="border" borderRadius={8} borderWidth={1} p={2}>
<Flex alignItems="center" justifyContent="space-between" mb={2}>
<HStack alignItems="center" gap={2}>
<TaskIcon height={8} width={8} />
<Heading size="lg">
<strong>Task: </strong>
{task.task_display_name}
{task.is_mapped ? "[ ]" : ""}
</Heading>
<Flex>
<div />
</Flex>
</HStack>
{task.doc_md === null ? undefined : (
<DagDocumentation docMd={task.doc_md} />
)}
</Flex>
<SimpleGrid columns={4} gap={4}>
<Stat label="Operator">
<Text>{task.operator_name}</Text>
</Stat>
<Stat label="Trigger Rule">
<Text>{task.trigger_rule}</Text>
</Stat>
</SimpleGrid>
</Box>
);
Loading

0 comments on commit 410b930

Please sign in to comment.