Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion airflow/ui/src/components/Assets/AssetEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const AssetEvent = ({
}

return (
<Box fontSize={13} mt={1} w="full">
<Box borderBottomWidth={1} fontSize={13} mt={1} p={2}>
<Text fontWeight="bold">
<Time datetime={event.timestamp} />
</Text>
Expand Down
68 changes: 41 additions & 27 deletions airflow/ui/src/components/Assets/AssetEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,46 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Box, Heading, Flex, HStack, VStack, StackSeparator, Skeleton } from "@chakra-ui/react";
import { Box, Heading, Flex, HStack, Skeleton } from "@chakra-ui/react";
import { createListCollection } from "@chakra-ui/react/collection";
import { FiDatabase } from "react-icons/fi";

import { useAssetServiceGetAssetEvents } from "openapi/queries";
import type { AssetEventCollectionResponse, AssetEventResponse } from "openapi/requests/types.gen";
import { StateBadge } from "src/components/StateBadge";
import { Select } from "src/components/ui";
import { pluralize } from "src/utils";

import { DataTable } from "../DataTable";
import type { CardDef, TableState } from "../DataTable/types";
import { AssetEvent } from "./AssetEvent";

const cardDef = (assetId?: number): CardDef<AssetEventResponse> => ({
card: ({ row }) => <AssetEvent assetId={assetId} event={row} />,
meta: {
customSkeleton: <Skeleton height="120px" width="100%" />,
},
});

type AssetEventProps = {
readonly assetId?: number;
readonly data?: AssetEventCollectionResponse;
readonly endDate?: string;
readonly orderBy?: string;
readonly setOrderBy?: React.Dispatch<React.SetStateAction<string>>;
readonly startDate?: string;
readonly isLoading?: boolean;
readonly setOrderBy?: (order: string) => void;
readonly setTableUrlState?: (state: TableState) => void;
readonly tableUrlState?: TableState;
readonly title?: string;
};

export const AssetEvents = ({ assetId, endDate, orderBy, setOrderBy, startDate }: AssetEventProps) => {
const { data, isLoading } = useAssetServiceGetAssetEvents({
assetId,
limit: 6,
orderBy,
timestampGte: startDate,
timestampLte: endDate,
});

export const AssetEvents = ({
assetId,
data,
isLoading,
setOrderBy,
setTableUrlState,
tableUrlState,
title,
}: AssetEventProps) => {
const assetSortOptions = createListCollection({
items: [
{ label: "Newest first", value: "-timestamp" },
Expand All @@ -51,15 +64,15 @@ export const AssetEvents = ({ assetId, endDate, orderBy, setOrderBy, startDate }
});

return (
<Box borderRadius={5} borderWidth={1} ml={2} pb={2}>
<Box borderBottomWidth={0} borderRadius={5} borderWidth={1} ml={2}>
<Flex justify="space-between" mr={1} mt={0} pl={3} pt={1}>
<HStack>
<StateBadge colorPalette="blue" fontSize="md" variant="solid">
<FiDatabase />
{data?.total_entries ?? " "}
</StateBadge>
<Heading marginEnd="auto" size="md">
Asset Events
{pluralize(title ?? "Asset Event", data?.total_entries ?? 0, undefined, true)}
</Heading>
</HStack>
{setOrderBy === undefined ? undefined : (
Expand All @@ -85,17 +98,18 @@ export const AssetEvents = ({ assetId, endDate, orderBy, setOrderBy, startDate }
</Select.Root>
)}
</Flex>
{isLoading ? (
<VStack px={3} separator={<StackSeparator />}>
{Array.from({ length: 5 }, (_, index) => index).map((index) => (
<Skeleton height={100} key={index} width="full" />
))}
</VStack>
) : (
<VStack px={3} separator={<StackSeparator />}>
{data?.asset_events.map((event) => <AssetEvent assetId={assetId} event={event} key={event.id} />)}
</VStack>
)}
<DataTable
cardDef={cardDef(assetId)}
columns={[]}
data={data?.asset_events ?? []}
displayMode="card"
initialState={tableUrlState}
isLoading={isLoading}
modelName="Asset Event"
onStateChange={setTableUrlState}
skeletonCount={5}
total={data?.total_entries}
/>
</Box>
);
};
5 changes: 3 additions & 2 deletions airflow/ui/src/components/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ export const DataTable = <TData,>({
const display = displayMode === "card" && Boolean(cardDef) ? "card" : "table";
const hasRows = rows.length > 0;
const hasPagination =
table.getState().pagination.pageIndex !== 0 ||
(table.getState().pagination.pageIndex === 0 && rows.length !== total);
initialState?.pagination !== undefined &&
(table.getState().pagination.pageIndex !== 0 ||
(table.getState().pagination.pageIndex === 0 && rows.length !== total));

return (
<>
Expand Down
46 changes: 43 additions & 3 deletions airflow/ui/src/pages/Asset/Asset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
*/
import { Box, HStack } from "@chakra-ui/react";
import { ReactFlowProvider } from "@xyflow/react";
import { useCallback } from "react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { useParams } from "react-router-dom";

import { useAssetServiceGetAsset } from "openapi/queries";
import { useAssetServiceGetAsset, useAssetServiceGetAssetEvents } from "openapi/queries";
import { AssetEvents } from "src/components/Assets/AssetEvents";
import { BreadcrumbStats } from "src/components/BreadcrumbStats";
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
import { ProgressBar, Toaster } from "src/components/ui";

import { AssetGraph } from "./AssetGraph";
Expand All @@ -33,6 +35,11 @@ import { Header } from "./Header";
export const Asset = () => {
const { assetId } = useParams();

const { setTableURLState, tableURLState } = useTableURLState();
const { pagination, sorting } = tableURLState;
const [sort] = sorting;
const orderBy = sort ? `${sort.desc ? "-" : ""}${sort.id}` : "-timestamp";

const { data: asset, isLoading } = useAssetServiceGetAsset(
{ assetId: assetId === undefined ? 0 : parseInt(assetId, 10) },
undefined,
Expand All @@ -49,6 +56,32 @@ export const Asset = () => {
},
];

const { data, isLoading: isLoadingEvents } = useAssetServiceGetAssetEvents(
{
assetId: asset?.id,
limit: pagination.pageSize,
offset: pagination.pageIndex * pagination.pageSize,
orderBy,
},
undefined,
{ enabled: Boolean(asset?.id) },
);

const setOrderBy = useCallback(
(value: string) => {
setTableURLState({
pagination,
sorting: [
{
desc: value.startsWith("-"),
id: value.replace("-", ""),
},
],
});
},
[pagination, setTableURLState],
);

return (
<ReactFlowProvider>
<Toaster />
Expand All @@ -69,8 +102,15 @@ export const Asset = () => {
</PanelResizeHandle>
<Panel defaultSize={30} minSize={20}>
<Header asset={asset} />
<Box h="100%" overflow="auto" px={2}>
<AssetEvents assetId={asset?.id} />
<Box h="100%" overflow="auto" pt={2}>
<AssetEvents
assetId={asset?.id}
data={data}
isLoading={isLoadingEvents}
setOrderBy={setOrderBy}
setTableUrlState={setTableURLState}
tableUrlState={tableURLState}
/>
</Box>
</Panel>
</PanelGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import dayjs from "dayjs";
import { useState } from "react";
import { PiBooks } from "react-icons/pi";

import { useDashboardServiceHistoricalMetrics } from "openapi/queries";
import { useAssetServiceGetAssetEvents, useDashboardServiceHistoricalMetrics } from "openapi/queries";
import { AssetEvents } from "src/components/Assets/AssetEvents";
import { ErrorAlert } from "src/components/ErrorAlert";
import TimeRangeSelector from "src/components/TimeRangeSelector";
Expand Down Expand Up @@ -51,6 +51,13 @@ export const HistoricalMetrics = () => {
? Object.values(data.task_instance_states).reduce((partialSum, value) => partialSum + value, 0)
: 0;

const { data: assetEventsData, isLoading: isLoadingAssetEvents } = useAssetServiceGetAssetEvents({
limit: 6,
orderBy: assetSortBy,
timestampGte: startDate,
timestampLte: endDate,
});

return (
<Box width="100%">
<Flex color="fg.muted" my={2}>
Expand Down Expand Up @@ -90,10 +97,10 @@ export const HistoricalMetrics = () => {
</GridItem>
<GridItem colSpan={{ base: 3 }}>
<AssetEvents
data={assetEventsData}
endDate={endDate}
orderBy={assetSortBy}
isLoading={isLoadingAssetEvents}
setOrderBy={setAssetSortBy}
startDate={startDate}
/>
</GridItem>
</SimpleGrid>
Expand Down
25 changes: 20 additions & 5 deletions airflow/ui/src/pages/Run/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,40 @@
import { Box, Flex, HStack, Table, Text } from "@chakra-ui/react";
import { useParams } from "react-router-dom";

import { useDagRunServiceGetDagRun } from "openapi/queries";
import { useDagRunServiceGetDagRun, useDagRunServiceGetUpstreamAssetEvents } from "openapi/queries";
import { AssetEvents } from "src/components/Assets/AssetEvents";
import RenderedJsonField from "src/components/RenderedJsonField";
import { RunTypeIcon } from "src/components/RunTypeIcon";
import { StateBadge } from "src/components/StateBadge";
import Time from "src/components/Time";
import { ClipboardRoot, ClipboardIconButton } from "src/components/ui";
import { getDuration } from "src/utils";
import { getDuration, isStatePending, useAutoRefresh } from "src/utils";

export const Details = () => {
const { dagId = "", runId = "" } = useParams();

const { data: dagRun } = useDagRunServiceGetDagRun({
dagId,
dagRunId: runId,
const refetchInterval = useAutoRefresh({ dagId });

const { data: dagRun } = useDagRunServiceGetDagRun(
{
dagId,
dagRunId: runId,
},
undefined,
{ refetchInterval: (query) => (isStatePending(query.state.data?.state) ? refetchInterval : false) },
);

const { data, isLoading } = useDagRunServiceGetUpstreamAssetEvents({ dagId, dagRunId: runId }, undefined, {
enabled: dagRun?.run_type === "asset_triggered",
refetchInterval: () => (isStatePending(dagRun?.state) ? refetchInterval : false),
});

// TODO : Render DagRun configuration object
return (
<Box p={2}>
{data === undefined || dagRun?.run_type !== "asset_triggered" ? undefined : (
<AssetEvents data={data} isLoading={isLoading} title="Source Asset Event" />
)}
{dagRun === undefined ? (
<div />
) : (
Expand Down
16 changes: 16 additions & 0 deletions airflow/ui/src/pages/TaskInstance/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import { Box, Flex, HStack, Table, Heading } from "@chakra-ui/react";
import { useParams, useSearchParams } from "react-router-dom";

import {
useAssetServiceGetAssetEvents,
useTaskInstanceServiceGetMappedTaskInstance,
useTaskInstanceServiceGetTaskInstanceTryDetails,
} from "openapi/queries";
import { AssetEvents } from "src/components/Assets/AssetEvents";
import { StateBadge } from "src/components/StateBadge";
import { TaskTrySelect } from "src/components/TaskTrySelect";
import Time from "src/components/Time";
Expand Down Expand Up @@ -72,8 +74,22 @@ export const Details = () => {
},
);

const { data: assetEventsData, isLoading: isLoadingAssetEvents } = useAssetServiceGetAssetEvents(
{
sourceDagId: dagId,
sourceMapIndex: parseInt(mapIndex, 10),
sourceRunId: runId,
sourceTaskId: taskId,
},
undefined,
{
refetchInterval: () => (isStatePending(taskInstance?.state) ? refetchInterval : false),
},
);

return (
<Box p={2}>
<AssetEvents data={assetEventsData} isLoading={isLoadingAssetEvents} title="Created Asset Event" />
{taskInstance === undefined || tryNumber === undefined || taskInstance.try_number <= 1 ? (
<div />
) : (
Expand Down