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
6 changes: 6 additions & 0 deletions airflow-core/src/airflow/api_fastapi/common/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from airflow.models.dag_favorite import DagFavorite
from airflow.models.dag_version import DagVersion
from airflow.models.dagrun import DagRun
from airflow.models.errors import ParseImportError
from airflow.models.hitl import HITLDetail
from airflow.models.pool import Pool
from airflow.models.taskinstance import TaskInstance
Expand Down Expand Up @@ -1058,3 +1059,8 @@ def _optional_boolean(value: bool | None) -> bool | None:
)
),
]

# Parse Import Errors
QueryParseImportErrorFilenamePatternSearch = Annotated[
_SearchParam, Depends(search_param_factory(ParseImportError.filename, "filename_pattern"))
]
Original file line number Diff line number Diff line change
Expand Up @@ -4052,6 +4052,18 @@ paths:
default:
- id
title: Order By
- name: filename_pattern
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\
\ Regular expressions are **not** supported."
title: Filename Pattern
description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\
\ Regular expressions are **not** supported."
responses:
'200':
description: Successful Response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from airflow.api_fastapi.common.parameters import (
QueryLimit,
QueryOffset,
QueryParseImportErrorFilenamePatternSearch,
SortParam,
)
from airflow.api_fastapi.common.router import AirflowRouter
Expand Down Expand Up @@ -126,12 +127,14 @@ def get_import_errors(
).dynamic_depends()
),
],
filename_pattern: QueryParseImportErrorFilenamePatternSearch,
session: SessionDep,
user: GetUserDep,
) -> ImportErrorCollectionResponse:
"""Get all import errors."""
import_errors_select, total_entries = paginated_select(
statement=select(ParseImportError),
filters=[filename_pattern],
order_by=order_by,
offset=offset,
limit=limit,
Expand Down Expand Up @@ -174,6 +177,7 @@ def get_import_errors(
# Paginate the import errors query
import_errors_select, total_entries = paginated_select(
statement=import_errors_stmt,
filters=[filename_pattern],
order_by=order_by,
offset=offset,
limit=limit,
Expand Down
5 changes: 3 additions & 2 deletions airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,11 +596,12 @@ export const UseImportErrorServiceGetImportErrorKeyFn = ({ importErrorId }: {
export type ImportErrorServiceGetImportErrorsDefaultResponse = Awaited<ReturnType<typeof ImportErrorService.getImportErrors>>;
export type ImportErrorServiceGetImportErrorsQueryResult<TData = ImportErrorServiceGetImportErrorsDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>;
export const useImportErrorServiceGetImportErrorsKey = "ImportErrorServiceGetImportErrors";
export const UseImportErrorServiceGetImportErrorsKeyFn = ({ limit, offset, orderBy }: {
export const UseImportErrorServiceGetImportErrorsKeyFn = ({ filenamePattern, limit, offset, orderBy }: {
filenamePattern?: string;
limit?: number;
offset?: number;
orderBy?: string[];
} = {}, queryKey?: Array<unknown>) => [useImportErrorServiceGetImportErrorsKey, ...(queryKey ?? [{ limit, offset, orderBy }])];
} = {}, queryKey?: Array<unknown>) => [useImportErrorServiceGetImportErrorsKey, ...(queryKey ?? [{ filenamePattern, limit, offset, orderBy }])];
export type JobServiceGetJobsDefaultResponse = Awaited<ReturnType<typeof JobService.getJobs>>;
export type JobServiceGetJobsQueryResult<TData = JobServiceGetJobsDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>;
export const useJobServiceGetJobsKey = "JobServiceGetJobs";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1134,14 +1134,16 @@ export const ensureUseImportErrorServiceGetImportErrorData = (queryClient: Query
* @param data.limit
* @param data.offset
* @param data.orderBy
* @param data.filenamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns ImportErrorCollectionResponse Successful Response
* @throws ApiError
*/
export const ensureUseImportErrorServiceGetImportErrorsData = (queryClient: QueryClient, { limit, offset, orderBy }: {
export const ensureUseImportErrorServiceGetImportErrorsData = (queryClient: QueryClient, { filenamePattern, limit, offset, orderBy }: {
filenamePattern?: string;
limit?: number;
offset?: number;
orderBy?: string[];
} = {}) => queryClient.ensureQueryData({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ limit, offset, orderBy }), queryFn: () => ImportErrorService.getImportErrors({ limit, offset, orderBy }) });
} = {}) => queryClient.ensureQueryData({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ filenamePattern, limit, offset, orderBy }), queryFn: () => ImportErrorService.getImportErrors({ filenamePattern, limit, offset, orderBy }) });
/**
* Get Jobs
* Get all jobs.
Expand Down
6 changes: 4 additions & 2 deletions airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1134,14 +1134,16 @@ export const prefetchUseImportErrorServiceGetImportError = (queryClient: QueryCl
* @param data.limit
* @param data.offset
* @param data.orderBy
* @param data.filenamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns ImportErrorCollectionResponse Successful Response
* @throws ApiError
*/
export const prefetchUseImportErrorServiceGetImportErrors = (queryClient: QueryClient, { limit, offset, orderBy }: {
export const prefetchUseImportErrorServiceGetImportErrors = (queryClient: QueryClient, { filenamePattern, limit, offset, orderBy }: {
filenamePattern?: string;
limit?: number;
offset?: number;
orderBy?: string[];
} = {}) => queryClient.prefetchQuery({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ limit, offset, orderBy }), queryFn: () => ImportErrorService.getImportErrors({ limit, offset, orderBy }) });
} = {}) => queryClient.prefetchQuery({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ filenamePattern, limit, offset, orderBy }), queryFn: () => ImportErrorService.getImportErrors({ filenamePattern, limit, offset, orderBy }) });
/**
* Get Jobs
* Get all jobs.
Expand Down
6 changes: 4 additions & 2 deletions airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1134,14 +1134,16 @@ export const useImportErrorServiceGetImportError = <TData = Common.ImportErrorSe
* @param data.limit
* @param data.offset
* @param data.orderBy
* @param data.filenamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns ImportErrorCollectionResponse Successful Response
* @throws ApiError
*/
export const useImportErrorServiceGetImportErrors = <TData = Common.ImportErrorServiceGetImportErrorsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ limit, offset, orderBy }: {
export const useImportErrorServiceGetImportErrors = <TData = Common.ImportErrorServiceGetImportErrorsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ filenamePattern, limit, offset, orderBy }: {
filenamePattern?: string;
limit?: number;
offset?: number;
orderBy?: string[];
} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ limit, offset, orderBy }, queryKey), queryFn: () => ImportErrorService.getImportErrors({ limit, offset, orderBy }) as TData, ...options });
} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ filenamePattern, limit, offset, orderBy }, queryKey), queryFn: () => ImportErrorService.getImportErrors({ filenamePattern, limit, offset, orderBy }) as TData, ...options });
/**
* Get Jobs
* Get all jobs.
Expand Down
6 changes: 4 additions & 2 deletions airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1134,14 +1134,16 @@ export const useImportErrorServiceGetImportErrorSuspense = <TData = Common.Impor
* @param data.limit
* @param data.offset
* @param data.orderBy
* @param data.filenamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns ImportErrorCollectionResponse Successful Response
* @throws ApiError
*/
export const useImportErrorServiceGetImportErrorsSuspense = <TData = Common.ImportErrorServiceGetImportErrorsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ limit, offset, orderBy }: {
export const useImportErrorServiceGetImportErrorsSuspense = <TData = Common.ImportErrorServiceGetImportErrorsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ filenamePattern, limit, offset, orderBy }: {
filenamePattern?: string;
limit?: number;
offset?: number;
orderBy?: string[];
} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ limit, offset, orderBy }, queryKey), queryFn: () => ImportErrorService.getImportErrors({ limit, offset, orderBy }) as TData, ...options });
} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: Common.UseImportErrorServiceGetImportErrorsKeyFn({ filenamePattern, limit, offset, orderBy }, queryKey), queryFn: () => ImportErrorService.getImportErrors({ filenamePattern, limit, offset, orderBy }) as TData, ...options });
/**
* Get Jobs
* Get all jobs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2873,6 +2873,7 @@ export class ImportErrorService {
* @param data.limit
* @param data.offset
* @param data.orderBy
* @param data.filenamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns ImportErrorCollectionResponse Successful Response
* @throws ApiError
*/
Expand All @@ -2883,7 +2884,8 @@ export class ImportErrorService {
query: {
limit: data.limit,
offset: data.offset,
order_by: data.orderBy
order_by: data.orderBy,
filename_pattern: data.filenamePattern
},
errors: {
401: 'Unauthorized',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2895,6 +2895,10 @@ export type GetImportErrorData = {
export type GetImportErrorResponse = ImportErrorResponse;

export type GetImportErrorsData = {
/**
* SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
*/
filenamePattern?: string | null;
limit?: number;
offset?: number;
orderBy?: Array<(string)>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export const DAGImportErrors = ({ iconOnly = false }: { readonly iconOnly?: bool

const { data, error, isLoading } = useImportErrorServiceGetImportErrors();
const importErrorsCount = data?.total_entries ?? 0;
const importErrors = data?.import_errors ?? [];

if (isLoading) {
return <Skeleton height="9" width="225px" />;
Expand Down Expand Up @@ -70,7 +69,7 @@ export const DAGImportErrors = ({ iconOnly = false }: { readonly iconOnly?: bool
onClick={onOpen}
/>
)}
<DAGImportErrorsModal importErrors={importErrors} onClose={onClose} open={open} />
<DAGImportErrorsModal onClose={onClose} open={open} />
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,60 @@
* under the License.
*/
import { Heading, Text, HStack } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { LuFileWarning } from "react-icons/lu";
import { PiFilePy } from "react-icons/pi";

import type { ImportErrorResponse } from "openapi/requests/types.gen";
import { useImportErrorServiceGetImportErrors } from "openapi/queries";
import { SearchBar } from "src/components/SearchBar";
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 }) => {
export const DAGImportErrorsModal: React.FC<ImportDAGErrorModalProps> = ({ onClose, open }) => {
const [page, setPage] = useState(1);
const [searchQuery, setSearchQuery] = useState("");
const [filteredErrors, setFilteredErrors] = useState(importErrors);
const { t: translate } = useTranslation(["dashboard", "components"]);

const startRange = (page - 1) * PAGE_LIMIT;
const endRange = startRange + PAGE_LIMIT;
const visibleItems = filteredErrors.slice(startRange, endRange);
const { data } = useImportErrorServiceGetImportErrors({
filenamePattern: searchQuery || undefined,
limit: PAGE_LIMIT,
offset: PAGE_LIMIT * (page - 1),
});

const { t: translate } = useTranslation(["dashboard", "components"]);

const onOpenChange = () => {
setSearchQuery("");
setPage(1);
onClose();
};

useEffect(() => {
const query = searchQuery.toLowerCase();
const filtered = importErrors.filter((error) => error.filename.toLowerCase().includes(query));

setFilteredErrors(filtered);
const handleSearchChange = (value: string) => {
setSearchQuery(value);
setPage(1);
}, [searchQuery, importErrors]);
};

return (
<Dialog.Root onOpenChange={onOpenChange} open={open} scrollBehavior="inside" size="xl">
<Dialog.Content backdrop p={4}>
<Dialog.Header display="flex" justifyContent="space-between">
<HStack fontSize="xl">
<LuFileWarning />
<Heading>{translate("importErrors.dagImportError", { count: importErrors.length })}</Heading>
<Heading>{translate("importErrors.dagImportError", { count: data?.total_entries ?? 0 })}</Heading>
</HStack>
<SearchBar
buttonProps={{ disabled: true }}
defaultValue={searchQuery}
hideAdvanced
onChange={setSearchQuery}
onChange={handleSearchChange}
placeHolder={translate("importErrors.searchByFile")}
/>
</Dialog.Header>
Expand All @@ -81,7 +79,7 @@ export const DAGImportErrorsModal: React.FC<ImportDAGErrorModalProps> = ({ impor

<Dialog.Body>
<Accordion.Root collapsible multiple size="md" variant="enclosed">
{visibleItems.map((importError) => (
{data?.import_errors.map((importError) => (
<Accordion.Item key={importError.import_error_id} value={importError.filename}>
<Accordion.ItemTrigger cursor="pointer">
<Text display="flex" fontWeight="bold">
Expand All @@ -108,7 +106,7 @@ export const DAGImportErrorsModal: React.FC<ImportDAGErrorModalProps> = ({ impor
</Dialog.Body>

<Pagination.Root
count={filteredErrors.length}
count={data?.total_entries ?? 0}
onPageChange={(event) => setPage(event.page)}
p={4}
page={page}
Expand Down