This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 802705581cd AIP-38 | List Import Errors (#44021)
802705581cd is described below
commit 802705581cd2a59da49b379a868e007254849398
Author: Shubham Raj <[email protected]>
AuthorDate: Mon Nov 18 19:27:40 2024 +0530
AIP-38 | List Import Errors (#44021)
* create skeleton
* added modal
* add data in modal
* refactor
* time stamp
* handling reviews
* colour fix and search
* add skeleton and colour fix
* add error
* handle reviews
---
airflow/ui/src/pages/DagsList/DagCard.tsx | 6 +-
airflow/ui/src/pages/Dashboard/Dashboard.tsx | 4 +
.../ui/src/pages/Dashboard/{ => Health}/Health.tsx | 6 +-
.../pages/Dashboard/{ => Health}/HealthSection.tsx | 0
.../src/pages/Dashboard/{ => Health}/HealthTag.tsx | 0
.../Dashboard/{Dashboard.tsx => Health/index.ts} | 16 +--
.../src/pages/Dashboard/Stats/DAGImportErrors.tsx | 78 +++++++++++++
.../pages/Dashboard/Stats/DAGImportErrorsModal.tsx | 125 +++++++++++++++++++++
.../Dashboard/{Dashboard.tsx => Stats/Stats.tsx} | 22 ++--
.../Dashboard/{Dashboard.tsx => Stats/index.ts} | 16 +--
10 files changed, 226 insertions(+), 47 deletions(-)
diff --git a/airflow/ui/src/pages/DagsList/DagCard.tsx
b/airflow/ui/src/pages/DagsList/DagCard.tsx
index a2c67b4eb8c..13f15735991 100644
--- a/airflow/ui/src/pages/DagsList/DagCard.tsx
+++ b/airflow/ui/src/pages/DagsList/DagCard.tsx
@@ -79,13 +79,13 @@ export const DagCard = ({ dag }: Props) => {
</Flex>
<SimpleGrid columns={4} gap={4} height={20} px={3} py={2}>
<VStack align="flex-start" gap={1}>
- <Heading color="gray.500" fontSize="xs">
+ <Heading color="fg.muted" fontSize="xs">
Schedule
</Heading>
<Schedule dag={dag} />
</VStack>
<VStack align="flex-start" gap={1}>
- <Heading color="gray.500" fontSize="xs">
+ <Heading color="fg.muted" fontSize="xs">
Latest Run
</Heading>
{latestRun ? (
@@ -99,7 +99,7 @@ export const DagCard = ({ dag }: Props) => {
) : undefined}
</VStack>
<VStack align="flex-start" gap={1}>
- <Heading color="gray.500" fontSize="xs">
+ <Heading color="fg.muted" fontSize="xs">
Next Run
</Heading>
{Boolean(dag.next_dagrun) ? (
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
b/airflow/ui/src/pages/Dashboard/Dashboard.tsx
index 38727b0809e..39131f1969c 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Dashboard.tsx
@@ -20,6 +20,7 @@ import { Box, Heading } from "@chakra-ui/react";
import { Health } from "./Health";
import { HistoricalMetrics } from "./HistoricalMetrics";
+import { Stats } from "./Stats";
export const Dashboard = () => (
<Box>
@@ -27,6 +28,9 @@ export const Dashboard = () => (
<Box>
<Health />
</Box>
+ <Box mt={5}>
+ <Stats />
+ </Box>
<Box mt={5}>
<HistoricalMetrics />
</Box>
diff --git a/airflow/ui/src/pages/Dashboard/Health.tsx
b/airflow/ui/src/pages/Dashboard/Health/Health.tsx
similarity index 94%
rename from airflow/ui/src/pages/Dashboard/Health.tsx
rename to airflow/ui/src/pages/Dashboard/Health/Health.tsx
index d105b7035a8..6697d6e7d0d 100644
--- a/airflow/ui/src/pages/Dashboard/Health.tsx
+++ b/airflow/ui/src/pages/Dashboard/Health/Health.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Box, Flex, Heading, HStack } from "@chakra-ui/react";
-import { FiClipboard } from "react-icons/fi";
+import { MdOutlineHealthAndSafety } from "react-icons/md";
import { useMonitorServiceGetHealth } from "openapi/queries";
import { ErrorAlert } from "src/components/ErrorAlert";
@@ -29,8 +29,8 @@ export const Health = () => {
return (
<Box>
- <Flex color="gray.500" mb={2}>
- <FiClipboard />
+ <Flex color="fg.muted" mb={2}>
+ <MdOutlineHealthAndSafety />
<Heading ml={1} size="xs">
Health
</Heading>
diff --git a/airflow/ui/src/pages/Dashboard/HealthSection.tsx
b/airflow/ui/src/pages/Dashboard/Health/HealthSection.tsx
similarity index 100%
rename from airflow/ui/src/pages/Dashboard/HealthSection.tsx
rename to airflow/ui/src/pages/Dashboard/Health/HealthSection.tsx
diff --git a/airflow/ui/src/pages/Dashboard/HealthTag.tsx
b/airflow/ui/src/pages/Dashboard/Health/HealthTag.tsx
similarity index 100%
rename from airflow/ui/src/pages/Dashboard/HealthTag.tsx
rename to airflow/ui/src/pages/Dashboard/Health/HealthTag.tsx
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
b/airflow/ui/src/pages/Dashboard/Health/index.ts
similarity index 71%
copy from airflow/ui/src/pages/Dashboard/Dashboard.tsx
copy to airflow/ui/src/pages/Dashboard/Health/index.ts
index 38727b0809e..e738024b047 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Health/index.ts
@@ -16,19 +16,5 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box, Heading } from "@chakra-ui/react";
-import { Health } from "./Health";
-import { HistoricalMetrics } from "./HistoricalMetrics";
-
-export const Dashboard = () => (
- <Box>
- <Heading mb={4}>Welcome</Heading>
- <Box>
- <Health />
- </Box>
- <Box mt={5}>
- <HistoricalMetrics />
- </Box>
- </Box>
-);
+export { Health } from "./Health";
diff --git a/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx
b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx
new file mode 100644
index 00000000000..3d62b08d3c6
--- /dev/null
+++ b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx
@@ -0,0 +1,78 @@
+/*!
+ * 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,
+ Badge,
+ Text,
+ Button,
+ useDisclosure,
+ Skeleton,
+} from "@chakra-ui/react";
+import { FiChevronRight } from "react-icons/fi";
+
+import { useImportErrorServiceGetImportErrors } from "openapi/queries";
+import { ErrorAlert } from "src/components/ErrorAlert";
+
+import { DAGImportErrorsModal } from "./DAGImportErrorsModal";
+
+export const DAGImportErrors = () => {
+ const { onClose, onOpen, open } = useDisclosure();
+
+ const { data, error, isLoading } = useImportErrorServiceGetImportErrors();
+ const importErrorsCount = data?.total_entries ?? 0;
+ const importErrors = data?.import_errors ?? [];
+
+ if (isLoading) {
+ return <Skeleton height="9" width="225px" />;
+ }
+
+ return (
+ <Box alignItems="center" display="flex" gap={2}>
+ <ErrorAlert error={error} />
+ {importErrorsCount > 0 && (
+ <Button
+ alignItems="center"
+ borderRadius="md"
+ display="flex"
+ gap={2}
+ onClick={onOpen}
+ variant="outline"
+ >
+ <Badge
+ background="red.solid"
+ borderRadius="full"
+ color="red.contrast"
+ px={2}
+ >
+ {importErrorsCount}
+ </Badge>
+ <Box alignItems="center" display="flex" gap={1}>
+ <Text fontWeight="bold">Dag Import Errors</Text>
+ <FiChevronRight />
+ </Box>
+ </Button>
+ )}
+ <DAGImportErrorsModal
+ importErrors={importErrors}
+ onClose={onClose}
+ open={open}
+ />
+ </Box>
+ );
+};
diff --git a/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrorsModal.tsx
b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrorsModal.tsx
new file mode 100644
index 00000000000..2735864112e
--- /dev/null
+++ b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrorsModal.tsx
@@ -0,0 +1,125 @@
+/*!
+ * 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 { Heading, Text, HStack, Input } from "@chakra-ui/react";
+import { useEffect, useState } from "react";
+import { PiFilePy } from "react-icons/pi";
+
+import type { ImportErrorResponse } from "openapi/requests/types.gen";
+import Time from "src/components/Time";
+import { Accordion, Dialog } from "src/components/ui";
+import { Pagination } from "src/components/ui/Pagination";
+
+type ImportDAGErrorModalProps = {
+ importErrors: Array<ImportErrorResponse>;
+ onClose: () => void;
+ open: boolean;
+};
+
+const PAGE_LIMIT = 15;
+
+export const DAGImportErrorsModal: React.FC<ImportDAGErrorModalProps> = ({
+ importErrors,
+ onClose,
+ open,
+}) => {
+ const [page, setPage] = useState(1);
+ const [searchQuery, setSearchQuery] = useState("");
+ const [filteredErrors, setFilteredErrors] = useState(importErrors);
+
+ const startRange = (page - 1) * PAGE_LIMIT;
+ const endRange = startRange + PAGE_LIMIT;
+ const visibleItems = filteredErrors.slice(startRange, endRange);
+
+ const onOpenChange = () => {
+ setSearchQuery("");
+ setPage(1);
+ onClose();
+ };
+
+ useEffect(() => {
+ const query = searchQuery.toLowerCase();
+ const filtered = importErrors.filter((error) =>
+ error.filename.toLowerCase().includes(query),
+ );
+
+ setFilteredErrors(filtered);
+ setPage(1);
+ }, [searchQuery, importErrors]);
+
+ return (
+ <Dialog.Root
+ onOpenChange={onOpenChange}
+ open={open}
+ scrollBehavior="inside"
+ size="xl"
+ >
+ <Dialog.Content backdrop>
+ <Dialog.Header>
+ <Heading size="xl">Dag Import Errors</Heading>
+ <Input
+ mt={4}
+ onChange={(event) => setSearchQuery(event.target.value)}
+ placeholder="Search by file"
+ value={searchQuery}
+ />
+ </Dialog.Header>
+
+ <Dialog.CloseTrigger />
+
+ <Dialog.Body>
+ <Accordion.Root collapsible multiple size="md" variant="enclosed">
+ {visibleItems.map((importError) => (
+ <Accordion.Item
+ key={importError.import_error_id}
+ value={importError.filename}
+ >
+ <Accordion.ItemTrigger cursor="pointer">
+ <PiFilePy />
+ {importError.filename}
+ </Accordion.ItemTrigger>
+ <Accordion.ItemContent>
+ <Text color="fg.muted" fontSize="sm" mb={1}>
+ Timestamp: <Time datetime={importError.timestamp} />
+ </Text>
+ <Text color="fg.error" fontSize="sm" whiteSpace="pre-wrap">
+ <code>{importError.stack_trace}</code>
+ </Text>
+ </Accordion.ItemContent>
+ </Accordion.Item>
+ ))}
+ </Accordion.Root>
+ </Dialog.Body>
+
+ <Pagination.Root
+ count={filteredErrors.length}
+ onPageChange={(event) => setPage(event.page)}
+ p={4}
+ page={page}
+ pageSize={PAGE_LIMIT}
+ >
+ <HStack>
+ <Pagination.PrevTrigger />
+ <Pagination.Items />
+ <Pagination.NextTrigger />
+ </HStack>
+ </Pagination.Root>
+ </Dialog.Content>
+ </Dialog.Root>
+ );
+};
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
b/airflow/ui/src/pages/Dashboard/Stats/Stats.tsx
similarity index 70%
copy from airflow/ui/src/pages/Dashboard/Dashboard.tsx
copy to airflow/ui/src/pages/Dashboard/Stats/Stats.tsx
index 38727b0809e..69211367172 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Stats/Stats.tsx
@@ -16,19 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box, Heading } from "@chakra-ui/react";
+import { Box, Flex, Heading } from "@chakra-ui/react";
+import { FiClipboard } from "react-icons/fi";
-import { Health } from "./Health";
-import { HistoricalMetrics } from "./HistoricalMetrics";
+import { DAGImportErrors } from "./DAGImportErrors";
-export const Dashboard = () => (
+export const Stats = () => (
<Box>
- <Heading mb={4}>Welcome</Heading>
- <Box>
- <Health />
- </Box>
- <Box mt={5}>
- <HistoricalMetrics />
- </Box>
+ <Flex color="fg.muted" my={2}>
+ <FiClipboard />
+ <Heading ml={1} size="xs">
+ Stats
+ </Heading>
+ </Flex>
+ <DAGImportErrors />
</Box>
);
diff --git a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
b/airflow/ui/src/pages/Dashboard/Stats/index.ts
similarity index 71%
copy from airflow/ui/src/pages/Dashboard/Dashboard.tsx
copy to airflow/ui/src/pages/Dashboard/Stats/index.ts
index 38727b0809e..bda3388ef64 100644
--- a/airflow/ui/src/pages/Dashboard/Dashboard.tsx
+++ b/airflow/ui/src/pages/Dashboard/Stats/index.ts
@@ -16,19 +16,5 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box, Heading } from "@chakra-ui/react";
-import { Health } from "./Health";
-import { HistoricalMetrics } from "./HistoricalMetrics";
-
-export const Dashboard = () => (
- <Box>
- <Heading mb={4}>Welcome</Heading>
- <Box>
- <Health />
- </Box>
- <Box mt={5}>
- <HistoricalMetrics />
- </Box>
- </Box>
-);
+export { Stats } from "./Stats";