This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun 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 4f8cb6e328a AIP84: UI endpoint for config (#44265)
4f8cb6e328a is described below
commit 4f8cb6e328a1f99ef0550eff3b5d2ec94cb3260f
Author: vatsrahul1001 <[email protected]>
AuthorDate: Fri Nov 22 19:38:29 2024 +0530
AIP84: UI endpoint for config (#44265)
* AIP84:UI endpoint for config
* remove show_trigger_form_if_no_params
* flatten the dict
---
.../ui/__init__.py => datamodels/ui/config.py} | 32 +++-
.../api_fastapi/core_api/openapi/v1-generated.yaml | 106 +++++++++++
airflow/api_fastapi/core_api/routes/ui/__init__.py | 2 +
airflow/api_fastapi/core_api/routes/ui/config.py | 71 ++++++++
airflow/ui/openapi-gen/queries/common.ts | 94 +++++-----
airflow/ui/openapi-gen/queries/prefetch.ts | 113 ++++++------
airflow/ui/openapi-gen/queries/queries.ts | 151 +++++++++-------
airflow/ui/openapi-gen/queries/suspense.ts | 151 +++++++++-------
airflow/ui/openapi-gen/requests/schemas.gen.ts | 110 ++++++++++++
airflow/ui/openapi-gen/requests/services.gen.ts | 153 +++++++++-------
airflow/ui/openapi-gen/requests/types.gen.ts | 198 +++++++++++++--------
.../api_fastapi/core_api/routes/ui/test_config.py | 115 ++++++++++++
12 files changed, 919 insertions(+), 377 deletions(-)
diff --git a/airflow/api_fastapi/core_api/routes/ui/__init__.py
b/airflow/api_fastapi/core_api/datamodels/ui/config.py
similarity index 54%
copy from airflow/api_fastapi/core_api/routes/ui/__init__.py
copy to airflow/api_fastapi/core_api/datamodels/ui/config.py
index b7ebf9c5c46..48a140be7a2 100644
--- a/airflow/api_fastapi/core_api/routes/ui/__init__.py
+++ b/airflow/api_fastapi/core_api/datamodels/ui/config.py
@@ -16,13 +16,29 @@
# under the License.
from __future__ import annotations
-from airflow.api_fastapi.common.router import AirflowRouter
-from airflow.api_fastapi.core_api.routes.ui.assets import assets_router
-from airflow.api_fastapi.core_api.routes.ui.dags import dags_router
-from airflow.api_fastapi.core_api.routes.ui.dashboard import dashboard_router
+from pydantic import BaseModel
-ui_router = AirflowRouter(prefix="/ui")
-ui_router.include_router(assets_router)
-ui_router.include_router(dashboard_router)
-ui_router.include_router(dags_router)
+class ConfigResponse(BaseModel):
+ """configuration serializer."""
+
+ navbar_color: str
+ navbar_text_color: str
+ navbar_hover_color: str
+ navbar_text_hover_color: str
+ navbar_logo_text_color: str
+ page_size: int
+ auto_refresh_interval: int
+ default_ui_timezone: str
+ hide_paused_dags_by_default: bool
+ instance_name: str
+ instance_name_has_markup: bool
+ enable_swagger_ui: bool
+ require_confirmation_dag_change: bool
+ default_wrap: bool
+ warn_deployment_exposure: bool
+ audit_view_excluded_events: str
+ audit_view_included_events: str
+ is_k8s: bool
+ test_connection: str
+ state_color_mapping: dict
diff --git a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
index a3035c82208..ffb6b26e9dd 100644
--- a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
+++ b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
@@ -174,6 +174,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
+ /ui/config:
+ get:
+ tags:
+ - Config
+ summary: Get Configs
+ description: Get configs for UI.
+ operationId: get_configs
+ responses:
+ '200':
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ConfigResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/HTTPExceptionResponse'
/public/assets:
get:
tags:
@@ -5268,6 +5288,92 @@ components:
- value
title: ConfigOption
description: Config option.
+ ConfigResponse:
+ properties:
+ navbar_color:
+ type: string
+ title: Navbar Color
+ navbar_text_color:
+ type: string
+ title: Navbar Text Color
+ navbar_hover_color:
+ type: string
+ title: Navbar Hover Color
+ navbar_text_hover_color:
+ type: string
+ title: Navbar Text Hover Color
+ navbar_logo_text_color:
+ type: string
+ title: Navbar Logo Text Color
+ page_size:
+ type: integer
+ title: Page Size
+ auto_refresh_interval:
+ type: integer
+ title: Auto Refresh Interval
+ default_ui_timezone:
+ type: string
+ title: Default Ui Timezone
+ hide_paused_dags_by_default:
+ type: boolean
+ title: Hide Paused Dags By Default
+ instance_name:
+ type: string
+ title: Instance Name
+ instance_name_has_markup:
+ type: boolean
+ title: Instance Name Has Markup
+ enable_swagger_ui:
+ type: boolean
+ title: Enable Swagger Ui
+ require_confirmation_dag_change:
+ type: boolean
+ title: Require Confirmation Dag Change
+ default_wrap:
+ type: boolean
+ title: Default Wrap
+ warn_deployment_exposure:
+ type: boolean
+ title: Warn Deployment Exposure
+ audit_view_excluded_events:
+ type: string
+ title: Audit View Excluded Events
+ audit_view_included_events:
+ type: string
+ title: Audit View Included Events
+ is_k8s:
+ type: boolean
+ title: Is K8S
+ test_connection:
+ type: string
+ title: Test Connection
+ state_color_mapping:
+ type: object
+ title: State Color Mapping
+ type: object
+ required:
+ - navbar_color
+ - navbar_text_color
+ - navbar_hover_color
+ - navbar_text_hover_color
+ - navbar_logo_text_color
+ - page_size
+ - auto_refresh_interval
+ - default_ui_timezone
+ - hide_paused_dags_by_default
+ - instance_name
+ - instance_name_has_markup
+ - enable_swagger_ui
+ - require_confirmation_dag_change
+ - default_wrap
+ - warn_deployment_exposure
+ - audit_view_excluded_events
+ - audit_view_included_events
+ - is_k8s
+ - test_connection
+ - state_color_mapping
+ title: ConfigResponse
+ description: configuration serializer.
ConfigSection:
properties:
name:
diff --git a/airflow/api_fastapi/core_api/routes/ui/__init__.py
b/airflow/api_fastapi/core_api/routes/ui/__init__.py
index b7ebf9c5c46..156c1219996 100644
--- a/airflow/api_fastapi/core_api/routes/ui/__init__.py
+++ b/airflow/api_fastapi/core_api/routes/ui/__init__.py
@@ -18,6 +18,7 @@ from __future__ import annotations
from airflow.api_fastapi.common.router import AirflowRouter
from airflow.api_fastapi.core_api.routes.ui.assets import assets_router
+from airflow.api_fastapi.core_api.routes.ui.config import config_router
from airflow.api_fastapi.core_api.routes.ui.dags import dags_router
from airflow.api_fastapi.core_api.routes.ui.dashboard import dashboard_router
@@ -26,3 +27,4 @@ ui_router = AirflowRouter(prefix="/ui")
ui_router.include_router(assets_router)
ui_router.include_router(dashboard_router)
ui_router.include_router(dags_router)
+ui_router.include_router(config_router)
diff --git a/airflow/api_fastapi/core_api/routes/ui/config.py
b/airflow/api_fastapi/core_api/routes/ui/config.py
new file mode 100644
index 00000000000..b395440ba08
--- /dev/null
+++ b/airflow/api_fastapi/core_api/routes/ui/config.py
@@ -0,0 +1,71 @@
+# 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.
+from __future__ import annotations
+
+from typing import Any
+
+from fastapi import status
+
+from airflow.api_fastapi.common.router import AirflowRouter
+from airflow.api_fastapi.core_api.datamodels.ui.config import ConfigResponse
+from airflow.api_fastapi.core_api.openapi.exceptions import
create_openapi_http_exception_doc
+from airflow.configuration import conf
+from airflow.settings import IS_K8S_OR_K8SCELERY_EXECUTOR, STATE_COLORS
+
+config_router = AirflowRouter(tags=["Config"])
+
+WEBSERVER_CONFIG_KEYS = [
+ "navbar_color",
+ "page_size",
+ "auto_refresh_interval",
+ "default_ui_timezone",
+ "hide_paused_dags_by_default",
+ "warn_deployment_exposure",
+ "default_wrap",
+ "require_confirmation_dag_change",
+ "enable_swagger_ui",
+ "instance_name_has_markup",
+ "navbar_text_color",
+ "navbar_hover_color",
+ "navbar_text_hover_color",
+ "navbar_logo_text_color",
+]
+
+
+@config_router.get(
+ "/config",
+ include_in_schema=False,
+ responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
+)
+def get_configs() -> ConfigResponse:
+ """Get configs for UI."""
+ conf_dict = conf.as_dict()
+
+ config = {key: conf_dict["webserver"].get(key) for key in
WEBSERVER_CONFIG_KEYS}
+
+ additional_config: dict[str, Any] = {
+ "instance_name": conf.get("webserver", "instance_name",
fallback="Airflow"),
+ "audit_view_included_events": conf.get("webserver",
"audit_view_included_events", fallback=""),
+ "audit_view_excluded_events": conf.get("webserver",
"audit_view_excluded_events", fallback=""),
+ "test_connection": conf.get("core", "test_connection",
fallback="Disabled"),
+ "state_color_mapping": STATE_COLORS,
+ "is_k8s": IS_K8S_OR_K8SCELERY_EXECUTOR,
+ }
+
+ config.update({key: value for key, value in additional_config.items()})
+
+ return ConfigResponse.model_validate(config)
diff --git a/airflow/ui/openapi-gen/queries/common.ts
b/airflow/ui/openapi-gen/queries/common.ts
index 98f0b68505c..a8a5ac9b978 100644
--- a/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow/ui/openapi-gen/queries/common.ts
@@ -270,6 +270,59 @@ export const UseDagsServiceRecentDagRunsKeyFn = (
},
]),
];
+export type ConfigServiceGetConfigsDefaultResponse = Awaited<
+ ReturnType<typeof ConfigService.getConfigs>
+>;
+export type ConfigServiceGetConfigsQueryResult<
+ TData = ConfigServiceGetConfigsDefaultResponse,
+ TError = unknown,
+> = UseQueryResult<TData, TError>;
+export const useConfigServiceGetConfigsKey = "ConfigServiceGetConfigs";
+export const UseConfigServiceGetConfigsKeyFn = (queryKey?: Array<unknown>) => [
+ useConfigServiceGetConfigsKey,
+ ...(queryKey ?? []),
+];
+export type ConfigServiceGetConfigDefaultResponse = Awaited<
+ ReturnType<typeof ConfigService.getConfig>
+>;
+export type ConfigServiceGetConfigQueryResult<
+ TData = ConfigServiceGetConfigDefaultResponse,
+ TError = unknown,
+> = UseQueryResult<TData, TError>;
+export const useConfigServiceGetConfigKey = "ConfigServiceGetConfig";
+export const UseConfigServiceGetConfigKeyFn = (
+ {
+ accept,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ section?: string;
+ } = {},
+ queryKey?: Array<unknown>,
+) => [useConfigServiceGetConfigKey, ...(queryKey ?? [{ accept, section }])];
+export type ConfigServiceGetConfigValueDefaultResponse = Awaited<
+ ReturnType<typeof ConfigService.getConfigValue>
+>;
+export type ConfigServiceGetConfigValueQueryResult<
+ TData = ConfigServiceGetConfigValueDefaultResponse,
+ TError = unknown,
+> = UseQueryResult<TData, TError>;
+export const useConfigServiceGetConfigValueKey = "ConfigServiceGetConfigValue";
+export const UseConfigServiceGetConfigValueKeyFn = (
+ {
+ accept,
+ option,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ option: string;
+ section: string;
+ },
+ queryKey?: Array<unknown>,
+) => [
+ useConfigServiceGetConfigValueKey,
+ ...(queryKey ?? [{ accept, option, section }]),
+];
export type BackfillServiceListBackfillsDefaultResponse = Awaited<
ReturnType<typeof BackfillService.listBackfills>
>;
@@ -495,47 +548,6 @@ export const UseDagStatsServiceGetDagStatsKeyFn = (
} = {},
queryKey?: Array<unknown>,
) => [useDagStatsServiceGetDagStatsKey, ...(queryKey ?? [{ dagIds }])];
-export type ConfigServiceGetConfigDefaultResponse = Awaited<
- ReturnType<typeof ConfigService.getConfig>
->;
-export type ConfigServiceGetConfigQueryResult<
- TData = ConfigServiceGetConfigDefaultResponse,
- TError = unknown,
-> = UseQueryResult<TData, TError>;
-export const useConfigServiceGetConfigKey = "ConfigServiceGetConfig";
-export const UseConfigServiceGetConfigKeyFn = (
- {
- accept,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- section?: string;
- } = {},
- queryKey?: Array<unknown>,
-) => [useConfigServiceGetConfigKey, ...(queryKey ?? [{ accept, section }])];
-export type ConfigServiceGetConfigValueDefaultResponse = Awaited<
- ReturnType<typeof ConfigService.getConfigValue>
->;
-export type ConfigServiceGetConfigValueQueryResult<
- TData = ConfigServiceGetConfigValueDefaultResponse,
- TError = unknown,
-> = UseQueryResult<TData, TError>;
-export const useConfigServiceGetConfigValueKey = "ConfigServiceGetConfigValue";
-export const UseConfigServiceGetConfigValueKeyFn = (
- {
- accept,
- option,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- option: string;
- section: string;
- },
- queryKey?: Array<unknown>,
-) => [
- useConfigServiceGetConfigValueKey,
- ...(queryKey ?? [{ accept, option, section }]),
-];
export type DagWarningServiceListDagWarningsDefaultResponse = Awaited<
ReturnType<typeof DagWarningService.listDagWarnings>
>;
diff --git a/airflow/ui/openapi-gen/queries/prefetch.ts
b/airflow/ui/openapi-gen/queries/prefetch.ts
index ede67e5b80d..cfc40a363c5 100644
--- a/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -341,6 +341,68 @@ export const prefetchUseDagsServiceRecentDagRuns = (
tags,
}),
});
+/**
+ * Get Configs
+ * Get configs for UI.
+ * @returns ConfigResponse Successful Response
+ * @throws ApiError
+ */
+export const prefetchUseConfigServiceGetConfigs = (queryClient: QueryClient) =>
+ queryClient.prefetchQuery({
+ queryKey: Common.UseConfigServiceGetConfigsKeyFn(),
+ queryFn: () => ConfigService.getConfigs(),
+ });
+/**
+ * Get Config
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+export const prefetchUseConfigServiceGetConfig = (
+ queryClient: QueryClient,
+ {
+ accept,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ section?: string;
+ } = {},
+) =>
+ queryClient.prefetchQuery({
+ queryKey: Common.UseConfigServiceGetConfigKeyFn({ accept, section }),
+ queryFn: () => ConfigService.getConfig({ accept, section }),
+ });
+/**
+ * Get Config Value
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.option
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+export const prefetchUseConfigServiceGetConfigValue = (
+ queryClient: QueryClient,
+ {
+ accept,
+ option,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ option: string;
+ section: string;
+ },
+) =>
+ queryClient.prefetchQuery({
+ queryKey: Common.UseConfigServiceGetConfigValueKeyFn({
+ accept,
+ option,
+ section,
+ }),
+ queryFn: () => ConfigService.getConfigValue({ accept, option, section }),
+ });
/**
* List Backfills
* @param data The data for the request.
@@ -630,57 +692,6 @@ export const prefetchUseDagStatsServiceGetDagStats = (
queryKey: Common.UseDagStatsServiceGetDagStatsKeyFn({ dagIds }),
queryFn: () => DagStatsService.getDagStats({ dagIds }),
});
-/**
- * Get Config
- * @param data The data for the request.
- * @param data.section
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
-export const prefetchUseConfigServiceGetConfig = (
- queryClient: QueryClient,
- {
- accept,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- section?: string;
- } = {},
-) =>
- queryClient.prefetchQuery({
- queryKey: Common.UseConfigServiceGetConfigKeyFn({ accept, section }),
- queryFn: () => ConfigService.getConfig({ accept, section }),
- });
-/**
- * Get Config Value
- * @param data The data for the request.
- * @param data.section
- * @param data.option
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
-export const prefetchUseConfigServiceGetConfigValue = (
- queryClient: QueryClient,
- {
- accept,
- option,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- option: string;
- section: string;
- },
-) =>
- queryClient.prefetchQuery({
- queryKey: Common.UseConfigServiceGetConfigValueKeyFn({
- accept,
- option,
- section,
- }),
- queryFn: () => ConfigService.getConfigValue({ accept, option, section }),
- });
/**
* List Dag Warnings
* Get a list of DAG warnings.
diff --git a/airflow/ui/openapi-gen/queries/queries.ts
b/airflow/ui/openapi-gen/queries/queries.ts
index 29fa6562524..fb1d6568cf1 100644
--- a/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow/ui/openapi-gen/queries/queries.ts
@@ -429,6 +429,91 @@ export const useDagsServiceRecentDagRuns = <
}) as TData,
...options,
});
+/**
+ * Get Configs
+ * Get configs for UI.
+ * @returns ConfigResponse Successful Response
+ * @throws ApiError
+ */
+export const useConfigServiceGetConfigs = <
+ TData = Common.ConfigServiceGetConfigsDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useQuery<TData, TError>({
+ queryKey: Common.UseConfigServiceGetConfigsKeyFn(queryKey),
+ queryFn: () => ConfigService.getConfigs() as TData,
+ ...options,
+ });
+/**
+ * Get Config
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+export const useConfigServiceGetConfig = <
+ TData = Common.ConfigServiceGetConfigDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ {
+ accept,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ section?: string;
+ } = {},
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useQuery<TData, TError>({
+ queryKey: Common.UseConfigServiceGetConfigKeyFn(
+ { accept, section },
+ queryKey,
+ ),
+ queryFn: () => ConfigService.getConfig({ accept, section }) as TData,
+ ...options,
+ });
+/**
+ * Get Config Value
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.option
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+export const useConfigServiceGetConfigValue = <
+ TData = Common.ConfigServiceGetConfigValueDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ {
+ accept,
+ option,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ option: string;
+ section: string;
+ },
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useQuery<TData, TError>({
+ queryKey: Common.UseConfigServiceGetConfigValueKeyFn(
+ { accept, option, section },
+ queryKey,
+ ),
+ queryFn: () =>
+ ConfigService.getConfigValue({ accept, option, section }) as TData,
+ ...options,
+ });
/**
* List Backfills
* @param data The data for the request.
@@ -782,72 +867,6 @@ export const useDagStatsServiceGetDagStats = <
queryFn: () => DagStatsService.getDagStats({ dagIds }) as TData,
...options,
});
-/**
- * Get Config
- * @param data The data for the request.
- * @param data.section
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
-export const useConfigServiceGetConfig = <
- TData = Common.ConfigServiceGetConfigDefaultResponse,
- TError = unknown,
- TQueryKey extends Array<unknown> = unknown[],
->(
- {
- accept,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- section?: string;
- } = {},
- queryKey?: TQueryKey,
- options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
-) =>
- useQuery<TData, TError>({
- queryKey: Common.UseConfigServiceGetConfigKeyFn(
- { accept, section },
- queryKey,
- ),
- queryFn: () => ConfigService.getConfig({ accept, section }) as TData,
- ...options,
- });
-/**
- * Get Config Value
- * @param data The data for the request.
- * @param data.section
- * @param data.option
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
-export const useConfigServiceGetConfigValue = <
- TData = Common.ConfigServiceGetConfigValueDefaultResponse,
- TError = unknown,
- TQueryKey extends Array<unknown> = unknown[],
->(
- {
- accept,
- option,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- option: string;
- section: string;
- },
- queryKey?: TQueryKey,
- options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
-) =>
- useQuery<TData, TError>({
- queryKey: Common.UseConfigServiceGetConfigValueKeyFn(
- { accept, option, section },
- queryKey,
- ),
- queryFn: () =>
- ConfigService.getConfigValue({ accept, option, section }) as TData,
- ...options,
- });
/**
* List Dag Warnings
* Get a list of DAG warnings.
diff --git a/airflow/ui/openapi-gen/queries/suspense.ts
b/airflow/ui/openapi-gen/queries/suspense.ts
index ec5f2740800..af0ddd9a09a 100644
--- a/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow/ui/openapi-gen/queries/suspense.ts
@@ -409,6 +409,91 @@ export const useDagsServiceRecentDagRunsSuspense = <
}) as TData,
...options,
});
+/**
+ * Get Configs
+ * Get configs for UI.
+ * @returns ConfigResponse Successful Response
+ * @throws ApiError
+ */
+export const useConfigServiceGetConfigsSuspense = <
+ TData = Common.ConfigServiceGetConfigsDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useSuspenseQuery<TData, TError>({
+ queryKey: Common.UseConfigServiceGetConfigsKeyFn(queryKey),
+ queryFn: () => ConfigService.getConfigs() as TData,
+ ...options,
+ });
+/**
+ * Get Config
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+export const useConfigServiceGetConfigSuspense = <
+ TData = Common.ConfigServiceGetConfigDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ {
+ accept,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ section?: string;
+ } = {},
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useSuspenseQuery<TData, TError>({
+ queryKey: Common.UseConfigServiceGetConfigKeyFn(
+ { accept, section },
+ queryKey,
+ ),
+ queryFn: () => ConfigService.getConfig({ accept, section }) as TData,
+ ...options,
+ });
+/**
+ * Get Config Value
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.option
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+export const useConfigServiceGetConfigValueSuspense = <
+ TData = Common.ConfigServiceGetConfigValueDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ {
+ accept,
+ option,
+ section,
+ }: {
+ accept?: "application/json" | "text/plain" | "*/*";
+ option: string;
+ section: string;
+ },
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useSuspenseQuery<TData, TError>({
+ queryKey: Common.UseConfigServiceGetConfigValueKeyFn(
+ { accept, option, section },
+ queryKey,
+ ),
+ queryFn: () =>
+ ConfigService.getConfigValue({ accept, option, section }) as TData,
+ ...options,
+ });
/**
* List Backfills
* @param data The data for the request.
@@ -762,72 +847,6 @@ export const useDagStatsServiceGetDagStatsSuspense = <
queryFn: () => DagStatsService.getDagStats({ dagIds }) as TData,
...options,
});
-/**
- * Get Config
- * @param data The data for the request.
- * @param data.section
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
-export const useConfigServiceGetConfigSuspense = <
- TData = Common.ConfigServiceGetConfigDefaultResponse,
- TError = unknown,
- TQueryKey extends Array<unknown> = unknown[],
->(
- {
- accept,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- section?: string;
- } = {},
- queryKey?: TQueryKey,
- options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
-) =>
- useSuspenseQuery<TData, TError>({
- queryKey: Common.UseConfigServiceGetConfigKeyFn(
- { accept, section },
- queryKey,
- ),
- queryFn: () => ConfigService.getConfig({ accept, section }) as TData,
- ...options,
- });
-/**
- * Get Config Value
- * @param data The data for the request.
- * @param data.section
- * @param data.option
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
-export const useConfigServiceGetConfigValueSuspense = <
- TData = Common.ConfigServiceGetConfigValueDefaultResponse,
- TError = unknown,
- TQueryKey extends Array<unknown> = unknown[],
->(
- {
- accept,
- option,
- section,
- }: {
- accept?: "application/json" | "text/plain" | "*/*";
- option: string;
- section: string;
- },
- queryKey?: TQueryKey,
- options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
-) =>
- useSuspenseQuery<TData, TError>({
- queryKey: Common.UseConfigServiceGetConfigValueKeyFn(
- { accept, option, section },
- queryKey,
- ),
- queryFn: () =>
- ConfigService.getConfigValue({ accept, option, section }) as TData,
- ...options,
- });
/**
* List Dag Warnings
* Get a list of DAG warnings.
diff --git a/airflow/ui/openapi-gen/requests/schemas.gen.ts
b/airflow/ui/openapi-gen/requests/schemas.gen.ts
index 3a4e903b60d..0589b7a7cf3 100644
--- a/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -606,6 +606,116 @@ export const $ConfigOption = {
description: "Config option.",
} as const;
+export const $ConfigResponse = {
+ properties: {
+ navbar_color: {
+ type: "string",
+ title: "Navbar Color",
+ },
+ navbar_text_color: {
+ type: "string",
+ title: "Navbar Text Color",
+ },
+ navbar_hover_color: {
+ type: "string",
+ title: "Navbar Hover Color",
+ },
+ navbar_text_hover_color: {
+ type: "string",
+ title: "Navbar Text Hover Color",
+ },
+ navbar_logo_text_color: {
+ type: "string",
+ title: "Navbar Logo Text Color",
+ },
+ page_size: {
+ type: "integer",
+ title: "Page Size",
+ },
+ auto_refresh_interval: {
+ type: "integer",
+ title: "Auto Refresh Interval",
+ },
+ default_ui_timezone: {
+ type: "string",
+ title: "Default Ui Timezone",
+ },
+ hide_paused_dags_by_default: {
+ type: "boolean",
+ title: "Hide Paused Dags By Default",
+ },
+ instance_name: {
+ type: "string",
+ title: "Instance Name",
+ },
+ instance_name_has_markup: {
+ type: "boolean",
+ title: "Instance Name Has Markup",
+ },
+ enable_swagger_ui: {
+ type: "boolean",
+ title: "Enable Swagger Ui",
+ },
+ require_confirmation_dag_change: {
+ type: "boolean",
+ title: "Require Confirmation Dag Change",
+ },
+ default_wrap: {
+ type: "boolean",
+ title: "Default Wrap",
+ },
+ warn_deployment_exposure: {
+ type: "boolean",
+ title: "Warn Deployment Exposure",
+ },
+ audit_view_excluded_events: {
+ type: "string",
+ title: "Audit View Excluded Events",
+ },
+ audit_view_included_events: {
+ type: "string",
+ title: "Audit View Included Events",
+ },
+ is_k8s: {
+ type: "boolean",
+ title: "Is K8S",
+ },
+ test_connection: {
+ type: "string",
+ title: "Test Connection",
+ },
+ state_color_mapping: {
+ type: "object",
+ title: "State Color Mapping",
+ },
+ },
+ type: "object",
+ required: [
+ "navbar_color",
+ "navbar_text_color",
+ "navbar_hover_color",
+ "navbar_text_hover_color",
+ "navbar_logo_text_color",
+ "page_size",
+ "auto_refresh_interval",
+ "default_ui_timezone",
+ "hide_paused_dags_by_default",
+ "instance_name",
+ "instance_name_has_markup",
+ "enable_swagger_ui",
+ "require_confirmation_dag_change",
+ "default_wrap",
+ "warn_deployment_exposure",
+ "audit_view_excluded_events",
+ "audit_view_included_events",
+ "is_k8s",
+ "test_connection",
+ "state_color_mapping",
+ ],
+ title: "ConfigResponse",
+ description: "configuration serializer.",
+} as const;
+
export const $ConfigSection = {
properties: {
name: {
diff --git a/airflow/ui/openapi-gen/requests/services.gen.ts
b/airflow/ui/openapi-gen/requests/services.gen.ts
index dd305837ee8..71e9c729a7f 100644
--- a/airflow/ui/openapi-gen/requests/services.gen.ts
+++ b/airflow/ui/openapi-gen/requests/services.gen.ts
@@ -29,6 +29,11 @@ import type {
HistoricalMetricsResponse,
RecentDagRunsData,
RecentDagRunsResponse,
+ GetConfigsResponse,
+ GetConfigData,
+ GetConfigResponse,
+ GetConfigValueData,
+ GetConfigValueResponse,
ListBackfillsData,
ListBackfillsResponse,
CreateBackfillData,
@@ -69,10 +74,6 @@ import type {
GetDagSourceResponse,
GetDagStatsData,
GetDagStatsResponse,
- GetConfigData,
- GetConfigResponse,
- GetConfigValueData,
- GetConfigValueResponse,
ListDagWarningsData,
ListDagWarningsResponse,
GetDagsData,
@@ -560,6 +561,86 @@ export class DagsService {
}
}
+export class ConfigService {
+ /**
+ * Get Configs
+ * Get configs for UI.
+ * @returns ConfigResponse Successful Response
+ * @throws ApiError
+ */
+ public static getConfigs(): CancelablePromise<GetConfigsResponse> {
+ return __request(OpenAPI, {
+ method: "GET",
+ url: "/ui/config",
+ errors: {
+ 404: "Not Found",
+ },
+ });
+ }
+
+ /**
+ * Get Config
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+ public static getConfig(
+ data: GetConfigData = {},
+ ): CancelablePromise<GetConfigResponse> {
+ return __request(OpenAPI, {
+ method: "GET",
+ url: "/public/config",
+ headers: {
+ accept: data.accept,
+ },
+ query: {
+ section: data.section,
+ },
+ errors: {
+ 401: "Unauthorized",
+ 403: "Forbidden",
+ 404: "Not Found",
+ 406: "Not Acceptable",
+ 422: "Validation Error",
+ },
+ });
+ }
+
+ /**
+ * Get Config Value
+ * @param data The data for the request.
+ * @param data.section
+ * @param data.option
+ * @param data.accept
+ * @returns Config Successful Response
+ * @throws ApiError
+ */
+ public static getConfigValue(
+ data: GetConfigValueData,
+ ): CancelablePromise<GetConfigValueResponse> {
+ return __request(OpenAPI, {
+ method: "GET",
+ url: "/public/config/section/{section}/option/{option}",
+ path: {
+ section: data.section,
+ option: data.option,
+ },
+ headers: {
+ accept: data.accept,
+ },
+ errors: {
+ 401: "Unauthorized",
+ 403: "Forbidden",
+ 404: "Not Found",
+ 406: "Not Acceptable",
+ 422: "Validation Error",
+ },
+ });
+ }
+}
+
export class BackfillService {
/**
* List Backfills
@@ -1164,70 +1245,6 @@ export class DagStatsService {
}
}
-export class ConfigService {
- /**
- * Get Config
- * @param data The data for the request.
- * @param data.section
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
- public static getConfig(
- data: GetConfigData = {},
- ): CancelablePromise<GetConfigResponse> {
- return __request(OpenAPI, {
- method: "GET",
- url: "/public/config",
- headers: {
- accept: data.accept,
- },
- query: {
- section: data.section,
- },
- errors: {
- 401: "Unauthorized",
- 403: "Forbidden",
- 404: "Not Found",
- 406: "Not Acceptable",
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Get Config Value
- * @param data The data for the request.
- * @param data.section
- * @param data.option
- * @param data.accept
- * @returns Config Successful Response
- * @throws ApiError
- */
- public static getConfigValue(
- data: GetConfigValueData,
- ): CancelablePromise<GetConfigValueResponse> {
- return __request(OpenAPI, {
- method: "GET",
- url: "/public/config/section/{section}/option/{option}",
- path: {
- section: data.section,
- option: data.option,
- },
- headers: {
- accept: data.accept,
- },
- errors: {
- 401: "Unauthorized",
- 403: "Forbidden",
- 404: "Not Found",
- 406: "Not Acceptable",
- 422: "Validation Error",
- },
- });
- }
-}
-
export class DagWarningService {
/**
* List Dag Warnings
diff --git a/airflow/ui/openapi-gen/requests/types.gen.ts
b/airflow/ui/openapi-gen/requests/types.gen.ts
index f84d918d581..c844b3b56f8 100644
--- a/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -161,6 +161,34 @@ export type ConfigOption = {
value: string | [string, string];
};
+/**
+ * configuration serializer.
+ */
+export type ConfigResponse = {
+ navbar_color: string;
+ navbar_text_color: string;
+ navbar_hover_color: string;
+ navbar_text_hover_color: string;
+ navbar_logo_text_color: string;
+ page_size: number;
+ auto_refresh_interval: number;
+ default_ui_timezone: string;
+ hide_paused_dags_by_default: boolean;
+ instance_name: string;
+ instance_name_has_markup: boolean;
+ enable_swagger_ui: boolean;
+ require_confirmation_dag_change: boolean;
+ default_wrap: boolean;
+ warn_deployment_exposure: boolean;
+ audit_view_excluded_events: string;
+ audit_view_included_events: string;
+ is_k8s: boolean;
+ test_connection: string;
+ state_color_mapping: {
+ [key: string]: unknown;
+ };
+};
+
/**
* Config Section Schema.
*/
@@ -1239,6 +1267,23 @@ export type RecentDagRunsData = {
export type RecentDagRunsResponse = DAGWithLatestDagRunsCollectionResponse;
+export type GetConfigsResponse = ConfigResponse;
+
+export type GetConfigData = {
+ accept?: "application/json" | "text/plain" | "*/*";
+ section?: string | null;
+};
+
+export type GetConfigResponse = Config;
+
+export type GetConfigValueData = {
+ accept?: "application/json" | "text/plain" | "*/*";
+ option: string;
+ section: string;
+};
+
+export type GetConfigValueResponse = Config;
+
export type ListBackfillsData = {
dagId: string;
limit?: number;
@@ -1390,21 +1435,6 @@ export type GetDagStatsData = {
export type GetDagStatsResponse = DagStatsCollectionResponse;
-export type GetConfigData = {
- accept?: "application/json" | "text/plain" | "*/*";
- section?: string | null;
-};
-
-export type GetConfigResponse = Config;
-
-export type GetConfigValueData = {
- accept?: "application/json" | "text/plain" | "*/*";
- option: string;
- section: string;
-};
-
-export type GetConfigValueResponse = Config;
-
export type ListDagWarningsData = {
dagId?: string | null;
limit?: number;
@@ -2095,6 +2125,82 @@ export type $OpenApiTs = {
};
};
};
+ "/ui/config": {
+ get: {
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: ConfigResponse;
+ /**
+ * Not Found
+ */
+ 404: HTTPExceptionResponse;
+ };
+ };
+ };
+ "/public/config": {
+ get: {
+ req: GetConfigData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: Config;
+ /**
+ * Unauthorized
+ */
+ 401: HTTPExceptionResponse;
+ /**
+ * Forbidden
+ */
+ 403: HTTPExceptionResponse;
+ /**
+ * Not Found
+ */
+ 404: HTTPExceptionResponse;
+ /**
+ * Not Acceptable
+ */
+ 406: HTTPExceptionResponse;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
+ "/public/config/section/{section}/option/{option}": {
+ get: {
+ req: GetConfigValueData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: Config;
+ /**
+ * Unauthorized
+ */
+ 401: HTTPExceptionResponse;
+ /**
+ * Forbidden
+ */
+ 403: HTTPExceptionResponse;
+ /**
+ * Not Found
+ */
+ 404: HTTPExceptionResponse;
+ /**
+ * Not Acceptable
+ */
+ 406: HTTPExceptionResponse;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
"/public/backfills": {
get: {
req: ListBackfillsData;
@@ -2655,68 +2761,6 @@ export type $OpenApiTs = {
};
};
};
- "/public/config": {
- get: {
- req: GetConfigData;
- res: {
- /**
- * Successful Response
- */
- 200: Config;
- /**
- * Unauthorized
- */
- 401: HTTPExceptionResponse;
- /**
- * Forbidden
- */
- 403: HTTPExceptionResponse;
- /**
- * Not Found
- */
- 404: HTTPExceptionResponse;
- /**
- * Not Acceptable
- */
- 406: HTTPExceptionResponse;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/public/config/section/{section}/option/{option}": {
- get: {
- req: GetConfigValueData;
- res: {
- /**
- * Successful Response
- */
- 200: Config;
- /**
- * Unauthorized
- */
- 401: HTTPExceptionResponse;
- /**
- * Forbidden
- */
- 403: HTTPExceptionResponse;
- /**
- * Not Found
- */
- 404: HTTPExceptionResponse;
- /**
- * Not Acceptable
- */
- 406: HTTPExceptionResponse;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
"/public/dagWarnings": {
get: {
req: ListDagWarningsData;
diff --git a/tests/api_fastapi/core_api/routes/ui/test_config.py
b/tests/api_fastapi/core_api/routes/ui/test_config.py
new file mode 100644
index 00000000000..f5b3a3067e6
--- /dev/null
+++ b/tests/api_fastapi/core_api/routes/ui/test_config.py
@@ -0,0 +1,115 @@
+# 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.
+from __future__ import annotations
+
+from unittest.mock import patch
+
+import pytest
+
+mock_config_response = {
+ "navbar_color": "#fff",
+ "navbar_text_color": "#51504f",
+ "navbar_hover_color": "#eee",
+ "navbar_text_hover_color": "#51504f",
+ "navbar_logo_text_color": "#51504f",
+ "page_size": 100,
+ "auto_refresh_interval": 3,
+ "default_ui_timezone": "UTC",
+ "hide_paused_dags_by_default": False,
+ "instance_name": "Airflow",
+ "instance_name_has_markup": False,
+ "enable_swagger_ui": True,
+ "require_confirmation_dag_change": False,
+ "default_wrap": False,
+ "warn_deployment_exposure": False,
+ "audit_view_excluded_events": "",
+ "audit_view_included_events": "",
+ "is_k8s": False,
+ "test_connection": "Disabled",
+ "state_color_mapping": {
+ "deferred": "mediumpurple",
+ "failed": "red",
+ "queued": "gray",
+ "removed": "lightgrey",
+ "restarting": "violet",
+ "running": "lime",
+ "scheduled": "tan",
+ "skipped": "hotpink",
+ "success": "green",
+ "up_for_reschedule": "turquoise",
+ "up_for_retry": "gold",
+ "upstream_failed": "orange",
+ },
+}
+
+
[email protected]
+def mock_config_data():
+ """
+ Mock configuration settings used in the endpoint.
+ """
+ with patch("airflow.configuration.conf.as_dict") as mock_conf:
+ mock_conf.return_value = {
+ "webserver": {
+ "navbar_color": "#fff",
+ "navbar_text_color": "#51504f",
+ "navbar_hover_color": "#eee",
+ "navbar_text_hover_color": "#51504f",
+ "navbar_logo_text_color": "#51504f",
+ "page_size": "100",
+ "auto_refresh_interval": "3",
+ "default_ui_timezone": "UTC",
+ "hide_paused_dags_by_default": "false",
+ "instance_name": "Airflow",
+ "instance_name_has_markup": "false",
+ "enable_swagger_ui": "true",
+ "require_confirmation_dag_change": "false",
+ "default_wrap": "false",
+ "warn_deployment_exposure": "false",
+ "audit_view_excluded_events": "",
+ "audit_view_included_events": "",
+ }
+ }
+ with patch(
+ "airflow.settings.STATE_COLORS",
+ {
+ "deferred": "mediumpurple",
+ "failed": "red",
+ "queued": "gray",
+ "removed": "lightgrey",
+ "restarting": "violet",
+ "running": "lime",
+ "scheduled": "tan",
+ "skipped": "hotpink",
+ "success": "green",
+ "up_for_reschedule": "turquoise",
+ "up_for_retry": "gold",
+ "upstream_failed": "orange",
+ },
+ ):
+ yield mock_conf
+
+
+def test_get_configs_basic(mock_config_data, test_client):
+ """
+ Test the /ui/config endpoint to verify response matches mock data.
+ """
+
+ response = test_client.get("/ui/config")
+
+ assert response.status_code == 200
+ assert response.json() == mock_config_response