Skip to content
Draft
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
4 changes: 4 additions & 0 deletions airflow-core/src/airflow/plugins_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@
from airflow._shared.plugins_manager import (
AirflowPlugin as AirflowPlugin,
AirflowPluginSource as AirflowPluginSource,
ExternalViewDict as ExternalViewDict,
FastAPIAppDict as FastAPIAppDict,
FastAPIRootMiddlewareDict as FastAPIRootMiddlewareDict,
PluginsDirectorySource as PluginsDirectorySource,
ReactAppDict as ReactAppDict,
_load_entrypoint_plugins,
_load_plugins_from_plugin_directory,
is_valid_plugin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@

from __future__ import annotations

from typing import Annotated, Any
from typing import TYPE_CHECKING, Annotated, Any
from urllib.parse import urlparse

from airflow.plugins_manager import AirflowPlugin
from airflow.providers.common.compat.sdk import conf
from airflow.providers.common.compat.version_compat import AIRFLOW_V_3_1_PLUS

if TYPE_CHECKING:
from airflow.plugins_manager import FastAPIAppDict, ReactAppDict

_PLUGIN_PREFIX = "/hitl-review"


Expand Down Expand Up @@ -495,8 +498,8 @@ class HITLReviewPlugin(AirflowPlugin):
"""Register the HITL Review REST API + chat UI on the Airflow API server."""

name = "hitl_review"
fastapi_apps: list[dict[str, Any]] = []
react_apps: list[dict[str, str]] = []
fastapi_apps: list[FastAPIAppDict] = []
react_apps: list[ReactAppDict] = []
if AIRFLOW_V_3_1_PLUS:
fastapi_apps = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from __future__ import annotations

import sys
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING

from airflow.exceptions import AirflowConfigException
from airflow.providers.common.compat.sdk import AirflowPlugin, conf
Expand All @@ -28,11 +28,14 @@
if TYPE_CHECKING:
from sqlalchemy.orm import Session

from airflow.plugins_manager import FastAPIAppDict


from airflow.utils.db import DBLocks, create_global_lock


@provide_session
def _get_api_endpoint(*, session: Session = NEW_SESSION) -> dict[str, Any]:
def _get_api_endpoint(*, session: Session = NEW_SESSION) -> FastAPIAppDict:
# Ensure all required DB modeals are created before starting the API
with create_global_lock(session=session, lock=DBLocks.MIGRATIONS):
engine = session.get_bind().engine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
AirflowPluginException as AirflowPluginException,
AirflowPluginSource as AirflowPluginSource,
EntryPointSource as EntryPointSource,
ExternalViewDict as ExternalViewDict,
FastAPIAppDict as FastAPIAppDict,
FastAPIRootMiddlewareDict as FastAPIRootMiddlewareDict,
PluginsDirectorySource as PluginsDirectorySource,
ReactAppDict as ReactAppDict,
_load_entrypoint_plugins as _load_entrypoint_plugins,
_load_plugins_from_plugin_directory as _load_plugins_from_plugin_directory,
integrate_listener_plugins as integrate_listener_plugins,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@
import sys
import types
from pathlib import Path
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Literal, TypedDict

from typing_extensions import NotRequired

if TYPE_CHECKING:
if sys.version_info >= (3, 12):
from importlib import metadata
else:
import importlib_metadata as metadata
from collections.abc import Generator
from collections.abc import Callable, Generator
from types import ModuleType

from ..listeners.listener import ListenerManager
Expand Down Expand Up @@ -85,6 +87,48 @@ class AirflowPluginException(Exception):
"""Exception when loading plugin."""


class _BaseUIDict(TypedDict):
"""Shared UI fields mirroring ``BaseUIResponse``."""

name: str
icon: NotRequired[str]
icon_dark_mode: NotRequired[str]
url_route: NotRequired[str]
category: NotRequired[str]
nav_top_level: NotRequired[bool]


class ExternalViewDict(_BaseUIDict):
"""Dictionary structure for entries in AirflowPlugin.external_views."""

href: str
destination: NotRequired[Literal["nav", "dag", "dag_run", "task", "task_instance", "base"]]


class ReactAppDict(_BaseUIDict):
"""Dictionary structure for entries in AirflowPlugin.react_apps."""

bundle_url: str
destination: NotRequired[Literal["nav", "dag", "dag_run", "task", "task_instance", "base", "dashboard"]]


class FastAPIAppDict(TypedDict):
"""Dictionary structure for entries in AirflowPlugin.fastapi_apps."""

app: Any
url_prefix: str
name: NotRequired[str]


class FastAPIRootMiddlewareDict(TypedDict):
"""Dictionary structure for entries in AirflowPlugin.fastapi_root_middlewares."""

middleware: Any
args: NotRequired[list[Any]]
kwargs: NotRequired[dict[str, Any]]
name: NotRequired[str]


class AirflowPlugin:
"""Class used to define AirflowPlugin."""

Expand All @@ -98,13 +142,13 @@ class AirflowPlugin:
team_name: str | None = None

source: AirflowPluginSource | None = None
macros: list[Any] = []
macros: list[Callable[..., Any]] = []
admin_views: list[Any] = []
flask_blueprints: list[Any] = []
fastapi_apps: list[Any] = []
fastapi_root_middlewares: list[Any] = []
external_views: list[Any] = []
react_apps: list[Any] = []
fastapi_apps: list[FastAPIAppDict] = []
fastapi_root_middlewares: list[FastAPIRootMiddlewareDict] = []
external_views: list[ExternalViewDict] = []
react_apps: list[ReactAppDict] = []
menu_links: list[Any] = []
appbuilder_views: list[Any] = []
appbuilder_menu_items: list[Any] = []
Expand Down