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 7b183071a3 Fix Slack Connections created in the UI (#26845)
7b183071a3 is described below

commit 7b183071a398cbe340853f357bc6c029d551b4d1
Author: Andrey Anshin <[email protected]>
AuthorDate: Mon Oct 3 20:26:41 2022 +0400

    Fix Slack Connections created in the UI (#26845)
---
 airflow/providers/slack/hooks/slack.py         |  2 ++
 airflow/providers/slack/hooks/slack_webhook.py |  2 ++
 airflow/providers/slack/utils/__init__.py      |  7 ++++++-
 tests/providers/slack/utils/test_utils.py      | 23 +++++++++++++++++++++++
 4 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/airflow/providers/slack/hooks/slack.py 
b/airflow/providers/slack/hooks/slack.py
index 60d6ce78a9..7b145cfcc5 100644
--- a/airflow/providers/slack/hooks/slack.py
+++ b/airflow/providers/slack/hooks/slack.py
@@ -293,11 +293,13 @@ class SlackHook(BaseHook):
         from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
         from flask_babel import lazy_gettext
         from wtforms import IntegerField, StringField
+        from wtforms.validators import NumberRange, Optional
 
         return {
             prefixed_extra_field("timeout", cls.conn_type): IntegerField(
                 lazy_gettext("Timeout"),
                 widget=BS3TextFieldWidget(),
+                validators=[Optional(strip_whitespace=True), 
NumberRange(min=1)],
                 description="Optional. The maximum number of seconds the 
client will wait to connect "
                 "and receive a response from Slack API.",
             ),
diff --git a/airflow/providers/slack/hooks/slack_webhook.py 
b/airflow/providers/slack/hooks/slack_webhook.py
index d6659da381..a929ef6c25 100644
--- a/airflow/providers/slack/hooks/slack_webhook.py
+++ b/airflow/providers/slack/hooks/slack_webhook.py
@@ -420,11 +420,13 @@ class SlackWebhookHook(BaseHook):
         from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
         from flask_babel import lazy_gettext
         from wtforms import IntegerField, StringField
+        from wtforms.validators import NumberRange, Optional
 
         return {
             prefixed_extra_field("timeout", cls.conn_type): IntegerField(
                 lazy_gettext("Timeout"),
                 widget=BS3TextFieldWidget(),
+                validators=[Optional(), NumberRange(min=1)],
                 description="Optional. The maximum number of seconds the 
client will wait to connect "
                 "and receive a response from Slack Incoming Webhook.",
             ),
diff --git a/airflow/providers/slack/utils/__init__.py 
b/airflow/providers/slack/utils/__init__.py
index bfb9478ab3..6ae8143c87 100644
--- a/airflow/providers/slack/utils/__init__.py
+++ b/airflow/providers/slack/utils/__init__.py
@@ -54,7 +54,12 @@ class ConnectionExtraConfig:
         :param default: If specified then use as default value if field not 
present in Connection Extra.
         """
         prefixed_field = prefixed_extra_field(field, self.conn_type)
-        if prefixed_field in self.extra:
+        if prefixed_field in self.extra and self.extra[prefixed_field] not in 
(None, ""):
+            # Addition validation with non-empty required for connection which 
created in the UI
+            # in Airflow 2.2. In these connections always present key-value 
pair for all prefixed extras
+            # even if user do not fill this fields.
+            # In additional fields from `wtforms.IntegerField` might contain 
None value.
+            # E.g.: `{'extra__slackwebhook__proxy': '', 
'extra__slackwebhook__timeout': None}`
             return self.extra[prefixed_field]
         elif field in self.extra:
             return self.extra[field]
diff --git a/tests/providers/slack/utils/test_utils.py 
b/tests/providers/slack/utils/test_utils.py
index eb45ac2f6e..33eebef0e4 100644
--- a/tests/providers/slack/utils/test_utils.py
+++ b/tests/providers/slack/utils/test_utils.py
@@ -59,6 +59,29 @@ class TestConnectionExtra:
         )
         assert extra_config.get("arg1") == "bar"
 
+    @pytest.mark.parametrize("conn_type", ["slack", "slack_incoming_webhook"])
+    @pytest.mark.parametrize("empty_value", [None, ""])
+    def test_prefixed_extra_created_in_ui_connections(self, conn_type, 
empty_value):
+        """Test that empty strings or None values in UI ignored."""
+        extra_config = ConnectionExtraConfig(
+            conn_type=conn_type,
+            conn_id="test-conn-id",
+            extra={
+                f"extra__{conn_type}__arg_missing": empty_value,
+                "arg_extra": "bar",
+                f"extra__{conn_type}__arg_extra": empty_value,
+            },
+        )
+        error_message = (
+            r"Couldn't find '.*' or '.*' in Connection \('.*'\) Extra and no 
default value specified\."
+        )
+        with pytest.raises(KeyError, match=error_message):
+            # No fallback should raise an error
+            extra_config.get("arg_missing")
+
+        assert extra_config.get("arg_missing", default="foo") == "foo"
+        assert extra_config.get("arg_extra") == "bar"
+
     def test_get_parse_int(self):
         extra_config = ConnectionExtraConfig(
             conn_type="slack",

Reply via email to