Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more task details from rest api #37394

Merged
merged 1 commit into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 3 additions & 15 deletions airflow/www/static/js/api/useTaskInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,18 @@
*/

import axios, { AxiosResponse } from "axios";
import type { API, TaskInstance } from "src/types";
import type { API } from "src/types";
import { useQuery } from "react-query";
import { useAutoRefresh } from "src/context/autorefresh";

import { getMetaValue } from "src/utils";
import type { SetOptional } from "type-fest";

/* GridData.TaskInstance and API.TaskInstance are not compatible at the moment.
* Remove this function when changing the api response for grid_data_url to comply
* with API.TaskInstance.
*/
const convertTaskInstance = (ti: API.TaskInstance) =>
({ ...ti, runId: ti.dagRunId } as TaskInstance);

const taskInstanceApi = getMetaValue("task_instance_api");

interface Props
extends SetOptional<API.GetMappedTaskInstanceVariables, "mapIndex"> {
enabled: boolean;
enabled?: boolean;
}

const useTaskInstance = ({
Expand All @@ -61,15 +54,10 @@ const useTaskInstance = ({

return useQuery(
["taskInstance", dagId, dagRunId, taskId, mapIndex],
() =>
axios.get<AxiosResponse, API.TaskInstance>(url, {
headers: { Accept: "text/plain" },
}),
() => axios.get<AxiosResponse, API.TaskInstance>(url),
{
placeholderData: {},
refetchInterval: isRefreshOn && (autoRefreshInterval || 1) * 1000,
enabled,
select: convertTaskInstance,
}
);
};
Expand Down
12 changes: 10 additions & 2 deletions airflow/www/static/js/dag/details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,11 @@ const Details = ({
<MarkInstanceAs
taskId={taskId}
runId={runId}
state={instance?.state}
state={
!instance?.state || instance?.state === "none"
? undefined
: instance.state
}
isGroup={isGroup}
isMapped={isMapped}
mapIndex={mapIndex}
Expand Down Expand Up @@ -348,7 +352,11 @@ const Details = ({
mapIndex={mapIndex}
executionDate={run?.executionDate}
tryNumber={instance?.tryNumber}
state={instance?.state}
state={
!instance?.state || instance?.state === "none"
? undefined
: instance.state
}
/>
</TabPanel>
)}
Expand Down
147 changes: 120 additions & 27 deletions airflow/www/static/js/dag/details/taskInstance/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,37 @@
*/

import React from "react";
import { Text, Flex, Table, Tbody, Tr, Td, Divider } from "@chakra-ui/react";
import { Text, Flex, Table, Tbody, Tr, Td, Code } from "@chakra-ui/react";
import { snakeCase } from "lodash";

import { getGroupAndMapSummary } from "src/utils";
import { getDuration, formatDuration } from "src/datetime_utils";
import { SimpleStatus } from "src/dag/StatusBox";
import Time from "src/components/Time";
import { ClipboardText } from "src/components/Clipboard";
import type { Task, TaskInstance, TaskState } from "src/types";
import useTaskInstance from "src/api/useTaskInstance";
import type {
API,
Task,
TaskInstance as GridTaskInstance,
TaskState,
} from "src/types";
import DatasetUpdateEvents from "./DatasetUpdateEvents";

interface Props {
instance: TaskInstance;
gridInstance: GridTaskInstance;
taskInstance?: API.TaskInstance;
group: Task;
dagId: string;
}

const Details = ({ instance, group, dagId }: Props) => {
const Details = ({ gridInstance, taskInstance, group }: Props) => {
const isGroup = !!group.children;
const summary: React.ReactNode[] = [];

const { taskId, runId, startDate, endDate, state, mappedStates, mapIndex } =
instance;
const { taskId, runId, startDate, endDate, state } = gridInstance;

const { isMapped, tooltip, operator, hasOutletDatasets, triggerRule } = group;
const mappedStates = !taskInstance ? gridInstance.mappedStates : undefined;

const { data: apiTI } = useTaskInstance({
dagId,
dagRunId: runId,
taskId,
mapIndex,
enabled: !isGroup && !isMapped,
});
const { isMapped, tooltip, operator, hasOutletDatasets, triggerRule } = group;

const { totalTasks, childTaskMap } = getGroupAndMapSummary({
group,
Expand Down Expand Up @@ -83,37 +80,44 @@ const Details = ({ instance, group, dagId }: Props) => {
state &&
["success", "failed", "upstream_failed", "skipped"].includes(state);
const isOverall = (isMapped || isGroup) && "Overall ";

return (
<Flex flexWrap="wrap" justifyContent="space-between">
{state === "deferred" && (
{!!taskInstance?.trigger && !!taskInstance?.triggererJob && (
<>
<Text as="strong">Triggerer info</Text>
<Divider my={2} />
<Text as="strong" mb={3}>
Triggerer info
</Text>
<Table variant="striped" mb={3}>
<Tbody>
<Tr>
<Td>Trigger class</Td>
<Td>{`${apiTI?.trigger?.classpath}`}</Td>
<Td>{`${taskInstance?.trigger?.classpath}`}</Td>
</Tr>
<Tr>
<Td>Trigger ID</Td>
<Td>{`${taskInstance?.trigger?.id}`}</Td>
</Tr>
<Tr>
<Td>Trigger creation time</Td>
<Td>{`${apiTI?.trigger?.createdDate}`}</Td>
<Td>{`${taskInstance?.trigger?.createdDate}`}</Td>
</Tr>
<Tr>
<Td>Assigned triggerer</Td>
<Td>{`${apiTI?.triggererJob?.hostname}`}</Td>
<Td>{`${taskInstance?.triggererJob?.hostname}`}</Td>
</Tr>
<Tr>
<Td>Latest triggerer heartbeat</Td>
<Td>{`${apiTI?.triggererJob?.latestHeartbeat}`}</Td>
<Td>{`${taskInstance?.triggererJob?.latestHeartbeat}`}</Td>
</Tr>
</Tbody>
</Table>
</>
)}

<Text as="strong">Task Instance Details</Text>
<Divider my={2} />
<Text as="strong" mb={3}>
Task Instance Details
</Text>
<Table variant="striped">
<Tbody>
{tooltip && (
Expand Down Expand Up @@ -167,10 +171,16 @@ const Details = ({ instance, group, dagId }: Props) => {
</Text>
</Td>
</Tr>
{mapIndex !== undefined && (
{taskInstance?.mapIndex !== undefined && (
<Tr>
<Td>Map Index</Td>
<Td>{mapIndex}</Td>
<Td>{taskInstance.mapIndex}</Td>
</Tr>
)}
{!!taskInstance?.tryNumber && (
<Tr>
<Td>Try Number</Td>
<Td>{taskInstance.tryNumber}</Td>
</Tr>
)}
{operator && (
Expand Down Expand Up @@ -210,6 +220,89 @@ const Details = ({ instance, group, dagId }: Props) => {
</Td>
</Tr>
)}
{!!taskInstance?.pid && (
<Tr>
<Td>Process ID (PID)</Td>
<Td>
<ClipboardText value={taskInstance.pid.toString()} />
</Td>
</Tr>
)}
{!!taskInstance?.hostname && (
<Tr>
<Td>Hostname</Td>
<Td>
<ClipboardText value={taskInstance.hostname} />
</Td>
</Tr>
)}
{taskInstance?.renderedFields && (
<>
{Object.keys(taskInstance.renderedFields).map((key) => {
const renderedFields = taskInstance.renderedFields as Record<
string,
unknown
>;
if (renderedFields[key]) {
return (
<Tr key={key}>
<Td>{key}</Td>
<Td>
<Code fontSize="md">
{JSON.stringify(renderedFields[key])}
</Code>
</Td>
</Tr>
);
}
return null;
})}
</>
)}
{!!taskInstance?.pool && (
<Tr>
<Td>Pool</Td>
<Td>{taskInstance.pool}</Td>
</Tr>
)}
{!!taskInstance?.poolSlots && (
<Tr>
<Td>Pool Slots</Td>
<Td>{taskInstance.poolSlots}</Td>
</Tr>
)}
{!!taskInstance?.executorConfig && (
<Tr>
<Td>Executor Config</Td>
<Td>
<Code fontSize="md">{taskInstance.executorConfig}</Code>
</Td>
</Tr>
)}
{!!taskInstance?.unixname && (
<Tr>
<Td>Unix Name</Td>
<Td>{taskInstance.unixname}</Td>
</Tr>
)}
{!!taskInstance?.maxTries && (
<Tr>
<Td>Max Tries</Td>
<Td>{taskInstance.maxTries}</Td>
</Tr>
)}
{!!taskInstance?.queue && (
<Tr>
<Td>Queue</Td>
<Td>{taskInstance.queue}</Td>
</Tr>
)}
{!!taskInstance?.priorityWeight && (
<Tr>
<Td>Priority Weight</Td>
<Td>{taskInstance.priorityWeight}</Td>
</Tr>
)}
</Tbody>
</Table>
{hasOutletDatasets && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ const ExtraLinks = ({
url && /^(?:[a-z]+:)?\/\//.test(url);

return (
<Box mb={3}>
<Box my={3}>
<Text as="strong">Extra Links</Text>
<Divider my={2} />
<Flex flexWrap="wrap">
<Flex flexWrap="wrap" mt={3}>
{links.map(({ name, url }) => (
<Button
key={name}
Expand Down
42 changes: 17 additions & 25 deletions airflow/www/static/js/dag/details/taskInstance/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { Box } from "@chakra-ui/react";

import { useGridData, useTaskInstance } from "src/api";
import { getMetaValue, getTask, useOffsetTop } from "src/utils";
import type { DagRun, TaskInstance as TaskInstanceType } from "src/types";
import type { DagRun, TaskInstance as GridTaskInstance } from "src/types";
import NotesAccordion from "src/dag/details/NotesAccordion";

import TaskNav from "./Nav";
Expand All @@ -34,7 +34,7 @@ const dagId = getMetaValue("dag_id")!;
interface Props {
taskId: string;
runId: DagRun["runId"];
mapIndex: TaskInstanceType["mapIndex"];
mapIndex: GridTaskInstance["mapIndex"];
}

const TaskInstance = ({ taskId, runId, mapIndex }: Props) => {
Expand All @@ -56,19 +56,16 @@ const TaskInstance = ({ taskId, runId, mapIndex }: Props) => {
const isGroup = !!children;
const isGroupOrMappedTaskSummary = isGroup || isMappedTaskSummary;

const { data: mappedTaskInstance } = useTaskInstance({
const { data: taskInstance } = useTaskInstance({
dagId,
dagRunId: runId,
taskId,
mapIndex,
enabled: isMapIndexDefined,
enabled: (!isGroup && !isMapped) || isMapIndexDefined,
});
const gridInstance = group?.instances.find((ti) => ti.runId === runId);

const instance = isMapIndexDefined
? mappedTaskInstance
: group?.instances.find((ti) => ti.runId === runId);

if (!group || !run || !instance) return null;
if (!group || !run || !gridInstance) return null;

const { executionDate } = run;

Expand All @@ -94,31 +91,26 @@ const TaskInstance = ({ taskId, runId, mapIndex }: Props) => {
dagId={dagId}
runId={runId}
taskId={taskId}
mapIndex={instance.mapIndex}
initialValue={instance.note}
key={dagId + runId + taskId + instance.mapIndex}
/>
)}
{isMapped && group.extraLinks && isMapIndexDefined && (
<ExtraLinks
taskId={taskId}
dagId={dagId}
mapIndex={mapIndex}
executionDate={executionDate}
extraLinks={group?.extraLinks}
tryNumber={instance.tryNumber}
mapIndex={gridInstance.mapIndex}
initialValue={gridInstance.note}
key={dagId + runId + taskId + gridInstance.mapIndex}
/>
)}
{!isMapped && group.extraLinks && (
{!!group.extraLinks?.length && !isGroupOrMappedTaskSummary && (
<ExtraLinks
taskId={taskId}
dagId={dagId}
mapIndex={isMapped && isMapIndexDefined ? mapIndex : undefined}
executionDate={executionDate}
extraLinks={group?.extraLinks}
tryNumber={instance.tryNumber}
tryNumber={taskInstance?.tryNumber || gridInstance.tryNumber}
/>
)}
<Details instance={instance} group={group} dagId={dagId} />
<Details
gridInstance={gridInstance}
taskInstance={taskInstance}
group={group}
/>
</Box>
);
};
Expand Down