Skip to content

Commit 8f2f582

Browse files
committed
Add Data Quality provider UI plugin
1 parent 6af5e51 commit 8f2f582

52 files changed

Lines changed: 5740 additions & 20 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ repos:
783783
^airflow-core/src/airflow/ui/openapi-gen/|
784784
^airflow-core/src/airflow/ui/pnpm-lock\.yaml$|
785785
^providers/common/ai/src/airflow/providers/common/ai/plugins/www/pnpm-lock\.yaml$|
786+
^providers/common/dataquality/src/airflow/providers/common/dataquality/plugins/www/pnpm-lock\.yaml$|
786787
^airflow-core/src/airflow/ui/public/i18n/locales/de/README\.md$|
787788
^airflow-core/src/airflow/ui/src/i18n/config\.ts$|
788789
^\.agents/skills/airflow-translations/|

airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_plugins.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ class TestGetPlugins:
3939
# Filters
4040
(
4141
{},
42-
19,
42+
20,
4343
[
4444
"InformaticaProviderPlugin",
4545
"MetadataCollectionPlugin",
4646
"OpenLineageProviderPlugin",
4747
"business_day_window_plugin",
4848
"databricks_workflow",
49+
"dataquality",
4950
"decreasing_priority_weight_strategy_plugin",
5051
"edge_executor",
5152
"hitl_review",
@@ -64,14 +65,14 @@ class TestGetPlugins:
6465
),
6566
(
6667
{"limit": 3, "offset": 3},
67-
19,
68+
20,
6869
[
6970
"business_day_window_plugin",
7071
"databricks_workflow",
71-
"decreasing_priority_weight_strategy_plugin",
72+
"dataquality",
7273
],
7374
),
74-
({"limit": 1}, 19, ["InformaticaProviderPlugin"]),
75+
({"limit": 1}, 20, ["InformaticaProviderPlugin"]),
7576
],
7677
)
7778
def test_should_respond_200(
@@ -174,7 +175,7 @@ def test_invalid_external_view_destination_should_log_warning_and_continue(self,
174175
assert len(plugins_page) == 8
175176
assert "test_plugin_invalid" not in [p["name"] for p in plugins_page]
176177

177-
assert body["total_entries"] == 19
178+
assert body["total_entries"] == 20
178179

179180

180181
@skip_if_force_lowest_dependencies_marker

airflow-core/tests/unit/plugins/test_plugins_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ def test_does_not_double_import_entrypoint_provider_plugins(self):
408408
# Mock/skip loading from plugin dir
409409
with mock.patch("airflow.plugins_manager._load_plugins_from_plugin_directory", return_value=([], [])):
410410
plugins = plugins_manager._get_plugins()[0]
411-
assert len(plugins) == 7
411+
assert len(plugins) == 8
412412

413413

414414
class TestWindowPluginRegistration:

dev/breeze/src/airflow_breeze/commands/developer_commands.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
from airflow_breeze.utils.path_utils import (
128128
AIRFLOW_ROOT_PATH,
129129
COMMON_AI_PLUGIN_PREK_HOOK,
130+
COMMON_DATAQUALITY_PLUGIN_PREK_HOOK,
130131
EDGE_PLUGIN_PREK_HOOK,
131132
FAB_AUTH_MANAGER_WWW_PREK_HOOK,
132133
cleanup_python_generated_files,
@@ -663,7 +664,7 @@ def start_airflow(
663664
if use_airflow_version is None and not skip_assets_compilation:
664665
assert_prek_installed()
665666
# Compile provider assets if needed
666-
additional_assets = [COMMON_AI_PLUGIN_PREK_HOOK]
667+
additional_assets = [COMMON_AI_PLUGIN_PREK_HOOK, COMMON_DATAQUALITY_PLUGIN_PREK_HOOK]
667668
if executor and EDGE_EXECUTOR in executor:
668669
additional_assets.append(EDGE_PLUGIN_PREK_HOOK)
669670
if auth_manager == FAB_AUTH_MANAGER:

dev/breeze/src/airflow_breeze/utils/path_utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,22 @@ def find_airflow_root_path_to_operate_on() -> Path:
477477
COMMON_AI_UI_PLUGIN_DIST_PATH = COMMON_AI_PLUGIN_PATH / "dist"
478478
COMMON_AI_PLUGIN_PREK_HOOK = "compile-common-ai-provider-assets"
479479

480+
COMMON_DATAQUALITY_PLUGIN_PATH = (
481+
AIRFLOW_PROVIDERS_ROOT_PATH
482+
/ "common"
483+
/ "dataquality"
484+
/ "src"
485+
/ "airflow"
486+
/ "providers"
487+
/ "common"
488+
/ "dataquality"
489+
/ "plugins"
490+
/ "www"
491+
)
492+
COMMON_DATAQUALITY_UI_PLUGIN_NODE_MODULES_PATH = COMMON_DATAQUALITY_PLUGIN_PATH / "node_modules"
493+
COMMON_DATAQUALITY_UI_PLUGIN_DIST_PATH = COMMON_DATAQUALITY_PLUGIN_PATH / "dist"
494+
COMMON_DATAQUALITY_PLUGIN_PREK_HOOK = "compile-common-dataquality-provider-assets"
495+
480496

481497
DAGS_PATH = AIRFLOW_ROOT_PATH / "dags"
482498
FILES_PATH = AIRFLOW_ROOT_PATH / "files"

dev/breeze/src/airflow_breeze/utils/run_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
COMMON_AI_PLUGIN_PREK_HOOK,
4444
COMMON_AI_UI_PLUGIN_DIST_PATH,
4545
COMMON_AI_UI_PLUGIN_NODE_MODULES_PATH,
46+
COMMON_DATAQUALITY_PLUGIN_PREK_HOOK,
47+
COMMON_DATAQUALITY_UI_PLUGIN_DIST_PATH,
48+
COMMON_DATAQUALITY_UI_PLUGIN_NODE_MODULES_PATH,
4649
EDGE_PLUGIN_PREK_HOOK,
4750
EDGE_PLUGIN_UI_DIST_PATH,
4851
EDGE_PLUGIN_UI_NODE_MODULES_PATH,
@@ -528,6 +531,9 @@ def _clean_ui_assets(additional_ui_hooks: list[str]):
528531
if COMMON_AI_PLUGIN_PREK_HOOK in additional_ui_hooks:
529532
shutil.rmtree(COMMON_AI_UI_PLUGIN_NODE_MODULES_PATH, ignore_errors=True)
530533
shutil.rmtree(COMMON_AI_UI_PLUGIN_DIST_PATH, ignore_errors=True)
534+
if COMMON_DATAQUALITY_PLUGIN_PREK_HOOK in additional_ui_hooks:
535+
shutil.rmtree(COMMON_DATAQUALITY_UI_PLUGIN_NODE_MODULES_PATH, ignore_errors=True)
536+
shutil.rmtree(COMMON_DATAQUALITY_UI_PLUGIN_DIST_PATH, ignore_errors=True)
531537

532538
console_print("[success]Cleaned ui assets[/]")
533539

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
www-hash.txt
12
*.iml

providers/common/dataquality/.pre-commit-config.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ default_language_version:
2424
repos:
2525
- repo: local
2626
hooks:
27+
- id: ts-compile-lint-common-dataquality-ui
28+
name: Compile Data Quality UI
29+
description: Type-check changed TypeScript files in the Data Quality plugin UI
30+
language: node
31+
files: |
32+
(?x)
33+
^src/airflow/providers/common/dataquality/plugins/www/.*\.(js|ts|tsx|yaml|css|json)$
34+
exclude: |
35+
(?x)
36+
^src/airflow/providers/common/dataquality/plugins/www/node-modules/.*|
37+
^src/airflow/providers/common/dataquality/plugins/www/.pnpm-store
38+
entry: ../../../scripts/ci/prek/ts_compile_lint_common_dataquality.py
39+
additional_dependencies: ['pnpm@10.25.0']
40+
pass_filenames: true
41+
require_serial: true
42+
- id: compile-common-dataquality-provider-assets
43+
name: Compile Common Data Quality provider assets
44+
language: node
45+
stages: ['pre-commit', 'manual']
46+
files: ^src/airflow/providers/common/dataquality/plugins/www/
47+
entry: ../../../scripts/ci/prek/compile_provider_assets.py dataquality
48+
pass_filenames: false
49+
additional_dependencies: ['pnpm@10.25.0']
2750
- id: generate-common-dataquality-ruleset-schema
2851
name: Generate Data Quality RuleSet schema
2952
language: python
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
from __future__ import annotations
18+
19+
import logging
20+
import os
21+
import shutil
22+
from collections.abc import Callable, Iterable
23+
from pathlib import Path
24+
from subprocess import run
25+
from typing import Any
26+
27+
from hatchling.builders.config import BuilderConfig
28+
from hatchling.builders.plugin.interface import BuilderInterface
29+
from hatchling.plugin.manager import PluginManager
30+
31+
log = logging.getLogger(__name__)
32+
log_level = logging.getLevelName(os.getenv("CUSTOM_AIRFLOW_BUILD_LOG_LEVEL", "INFO"))
33+
log.setLevel(log_level)
34+
35+
36+
class CustomBuild(BuilderInterface[BuilderConfig, PluginManager]):
37+
"""Custom build class for Data Quality provider assets."""
38+
39+
PLUGIN_NAME = "custom"
40+
41+
@staticmethod
42+
def clean_dir(path: Path) -> None:
43+
log.warning("Cleaning directory: %s", path)
44+
shutil.rmtree(path, ignore_errors=True)
45+
46+
def clean(self, directory: str, versions: Iterable[str]) -> None:
47+
work_dir = Path(self.root)
48+
log.warning("Cleaning generated files in directory: %s", work_dir)
49+
dataquality_package_src = work_dir / "src" / "airflow" / "providers" / "common" / "dataquality"
50+
dataquality_ui_path = dataquality_package_src / "plugins" / "www"
51+
self.clean_dir(dataquality_ui_path / ".pnpm-store")
52+
self.clean_dir(dataquality_ui_path / "dist")
53+
self.clean_dir(dataquality_ui_path / "node_modules")
54+
(work_dir / "www-hash.txt").unlink(missing_ok=True)
55+
56+
def get_version_api(self) -> dict[str, Callable[..., str]]:
57+
"""Get custom build target for standard package preparation."""
58+
return {"standard": self.build_standard}
59+
60+
def build_standard(self, directory: str, artifacts: Any, **build_data: Any) -> str:
61+
work_dir = Path(self.root).parents[2].resolve()
62+
cmd = ["prek", "run", "compile-common-dataquality-provider-assets", "--all-files"]
63+
log.warning("Running command: %s", " ".join(cmd))
64+
run(cmd, cwd=work_dir.as_posix(), check=True)
65+
dist_path = (
66+
Path(self.root)
67+
/ "src"
68+
/ "airflow"
69+
/ "providers"
70+
/ "common"
71+
/ "dataquality"
72+
/ "plugins"
73+
/ "www"
74+
/ "dist"
75+
)
76+
return dist_path.resolve().as_posix()

providers/common/dataquality/provider.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ description: |
2929
state: ready
3030
lifecycle: incubation
3131
source-date-epoch: 1751587200
32-
build-system: flit_core
32+
build-system: hatchling
3333

3434
# Note that those versions are maintained by release manager - do not update them manually
3535
# with the exception of case where other provider in sources has >= new provider version.
@@ -54,6 +54,10 @@ task-decorators:
5454
- class-name: airflow.providers.common.dataquality.decorators.dq_check.dq_check_task
5555
name: dq_check
5656

57+
plugins:
58+
- name: dataquality
59+
plugin-class: airflow.providers.common.dataquality.plugins.dataquality_plugin.DataQualityPlugin
60+
5761
config:
5862
common.dataquality:
5963
description: |

0 commit comments

Comments
 (0)