You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2022/06/19 18:32:47 UTC
[airflow] branch main updated: Preserve original order of providers' connection extra fields in UI (#24425)
This is an automated email from the ASF dual-hosted git repository.
potiuk 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 9a3e1e47df Preserve original order of providers' connection extra fields in UI (#24425)
9a3e1e47df is described below
commit 9a3e1e47df0614e0b52f4166b8558fe39a57f4fe
Author: Andrey Anshin <An...@taragol.is>
AuthorDate: Sun Jun 19 22:32:27 2022 +0400
Preserve original order of providers' connection extra fields in UI (#24425)
* Preserve original order of providers' connection extra fields
* Sort widgets before printed in cli command `airflow providers widgets`
---
airflow/cli/commands/provider_command.py | 2 +-
airflow/providers_manager.py | 13 ++++++-
tests/core/test_providers_manager.py | 64 ++++++++++++++++++++++++++++++++
3 files changed, 76 insertions(+), 3 deletions(-)
diff --git a/airflow/cli/commands/provider_command.py b/airflow/cli/commands/provider_command.py
index b89f77fb5d..124c8cea8a 100644
--- a/airflow/cli/commands/provider_command.py
+++ b/airflow/cli/commands/provider_command.py
@@ -82,7 +82,7 @@ def hooks_list(args):
def connection_form_widget_list(args):
"""Lists all custom connection form fields at the command line"""
AirflowConsole().print_as(
- data=list(ProvidersManager().connection_form_widgets.items()),
+ data=list(sorted(ProvidersManager().connection_form_widgets.items())),
output=args.output,
mapper=lambda x: {
"connection_parameter_name": x[0],
diff --git a/airflow/providers_manager.py b/airflow/providers_manager.py
index b5d0297e90..eb7bd9676a 100644
--- a/airflow/providers_manager.py
+++ b/airflow/providers_manager.py
@@ -651,9 +651,15 @@ class ProvidersManager(LoggingMixin):
"""Force-import all hooks and initialize the connections/fields"""
# Retrieve all hooks to make sure that all of them are imported
_ = list(self._hooks_lazy_dict.values())
- self._connection_form_widgets = OrderedDict(sorted(self._connection_form_widgets.items()))
self._field_behaviours = OrderedDict(sorted(self._field_behaviours.items()))
+ # Widgets for connection forms are currently used in two places:
+ # 1. In the UI Connections, expected same order that it defined in Hook.
+ # 2. cli command - `airflow providers widgets` and expected that it in alphabetical order.
+ # It is not possible to recover original ordering after sorting,
+ # that the main reason why original sorting moved to cli part:
+ # self._connection_form_widgets = OrderedDict(sorted(self._connection_form_widgets.items()))
+
def _discover_taskflow_decorators(self) -> None:
for name, info in self._provider_dict.items():
for taskflow_decorator in info.data.get("task-decorators", []):
@@ -900,7 +906,10 @@ class ProvidersManager(LoggingMixin):
@property
def connection_form_widgets(self) -> Dict[str, ConnectionFormWidgetInfo]:
- """Returns widgets for connection forms."""
+ """
+ Returns widgets for connection forms.
+ Dictionary keys in the same order that it defined in Hook.
+ """
self.initialize_providers_hooks()
self._import_info_from_all_hooks()
return self._connection_form_widgets
diff --git a/tests/core/test_providers_manager.py b/tests/core/test_providers_manager.py
index a98dc9534e..857ed3f15c 100644
--- a/tests/core/test_providers_manager.py
+++ b/tests/core/test_providers_manager.py
@@ -188,6 +188,70 @@ class TestProviderManager:
)
assert provider_manager.connection_form_widgets['extra__test__my_param'].field == widget_field
+ def test_connection_form_widgets_fields_order(self):
+ """Check that order of connection for widgets preserved by original Hook order."""
+ test_conn_type = 'test'
+ field_prefix = f'extra__{test_conn_type}__'
+ field_names = ("yyy_param", "aaa_param", "000_param", "foo", "bar", "spam", "egg")
+
+ expected_field_names_order = tuple(f"{field_prefix}{f}" for f in field_names)
+
+ class TestHook:
+ conn_type = test_conn_type
+
+ provider_manager = ProvidersManager()
+ provider_manager._connection_form_widgets = {}
+ provider_manager._add_widgets(
+ package_name='mock',
+ hook_class=TestHook,
+ widgets={f: BooleanField(lazy_gettext('Dummy param')) for f in expected_field_names_order},
+ )
+ actual_field_names_order = tuple(
+ key for key in provider_manager.connection_form_widgets.keys() if key.startswith(field_prefix)
+ )
+ assert actual_field_names_order == expected_field_names_order, "Not keeping original fields order"
+
+ def test_connection_form_widgets_fields_order_multiple_hooks(self):
+ """
+ Check that order of connection for widgets preserved by original Hooks order.
+ Even if different hooks specified field with the same connection type.
+ """
+ test_conn_type = 'test'
+ field_prefix = f'extra__{test_conn_type}__'
+ field_names_hook_1 = ("foo", "bar", "spam", "egg")
+ field_names_hook_2 = ("yyy_param", "aaa_param", "000_param")
+
+ expected_field_names_order = tuple(
+ f"{field_prefix}{f}" for f in [*field_names_hook_1, *field_names_hook_2]
+ )
+
+ class TestHook1:
+ conn_type = test_conn_type
+
+ class TestHook2:
+ conn_type = 'another'
+
+ provider_manager = ProvidersManager()
+ provider_manager._connection_form_widgets = {}
+ provider_manager._add_widgets(
+ package_name='mock',
+ hook_class=TestHook1,
+ widgets={
+ f"{field_prefix}{f}": BooleanField(lazy_gettext('Dummy param')) for f in field_names_hook_1
+ },
+ )
+ provider_manager._add_widgets(
+ package_name='another_mock',
+ hook_class=TestHook2,
+ widgets={
+ f"{field_prefix}{f}": BooleanField(lazy_gettext('Dummy param')) for f in field_names_hook_2
+ },
+ )
+ actual_field_names_order = tuple(
+ key for key in provider_manager.connection_form_widgets.keys() if key.startswith(field_prefix)
+ )
+ assert actual_field_names_order == expected_field_names_order, "Not keeping original fields order"
+
def test_field_behaviours(self):
provider_manager = ProvidersManager()
connections_with_field_behaviours = list(provider_manager.field_behaviours.keys())