This is an automated email from the ASF dual-hosted git repository.
vincbeck 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 f09fbf30aaf Add return value to Slack operators for XCom (#63397)
f09fbf30aaf is described below
commit f09fbf30aaf53e90fdf0ad3601b8e6a21de2fc2a
Author: yuseok89 <[email protected]>
AuthorDate: Fri Mar 13 00:30:21 2026 +0900
Add return value to Slack operators for XCom (#63397)
---
.../src/airflow/providers/slack/operators/slack.py | 14 +++++-
.../slack/tests/unit/slack/operators/test_slack.py | 50 ++++++++++++++++++++++
2 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/providers/slack/src/airflow/providers/slack/operators/slack.py
b/providers/slack/src/airflow/providers/slack/operators/slack.py
index bcfa8c85c99..56b4c90a387 100644
--- a/providers/slack/src/airflow/providers/slack/operators/slack.py
+++ b/providers/slack/src/airflow/providers/slack/operators/slack.py
@@ -46,6 +46,8 @@ class SlackAPIOperator(BaseOperator):
:param base_url: A string representing the Slack API base URL. Optional
:param proxy: Proxy to make the Slack API call. Optional
:param retry_handlers: List of handlers to customize retry logic in
``slack_sdk.WebClient``. Optional
+
+ :return: The Slack API response. Returned value is pushed to XCom for
downstream tasks.
"""
def __init__(
@@ -101,7 +103,8 @@ class SlackAPIOperator(BaseOperator):
raise ValueError(msg)
if not self.api_params:
self.construct_api_call_params()
- self.hook.call(self.method, json=self.api_params)
+ response = self.hook.call(self.method, json=self.api_params)
+ return response.data
class SlackAPIPostOperator(SlackAPIOperator):
@@ -128,6 +131,9 @@ class SlackAPIPostOperator(SlackAPIOperator):
See https://api.slack.com/docs/attachments
:param thread_ts: Provide another message's ``ts`` value to make this
message a reply in a
thread. See https://api.slack.com/messaging#threading (templated)
+
+ :return: The Slack API response data (e.g. ``ts``, ``channel``,
``message``).
+ Returned value is pushed to XCom for downstream tasks.
"""
template_fields: Sequence[str] = ("username", "text", "attachments",
"blocks", "channel", "thread_ts")
@@ -223,6 +229,9 @@ class SlackAPIFileOperator(SlackAPIOperator):
:param method_version: The version of the method of Slack SDK Client to be
used, either "v1" or "v2".
:param thread_ts: Provide another message's ``ts`` value to upload the
file as a reply in a
thread. See https://api.slack.com/messaging#threading (templated)
+
+ :return: List of Slack API response data from ``files_upload_v2`` (one per
channel).
+ Returned value is pushed to XCom for downstream tasks.
"""
template_fields: Sequence[str] = (
@@ -279,7 +288,7 @@ class SlackAPIFileOperator(SlackAPIOperator):
)
def execute(self, context: Context):
- self.hook.send_file_v1_to_v2(
+ responses = self.hook.send_file_v1_to_v2(
channels=self.channels,
# For historical reason SlackAPIFileOperator use filename as
reference to file
file=self.filename,
@@ -290,3 +299,4 @@ class SlackAPIFileOperator(SlackAPIOperator):
snippet_type=self.snippet_type,
thread_ts=self.thread_ts,
)
+ return [r.data for r in responses]
diff --git a/providers/slack/tests/unit/slack/operators/test_slack.py
b/providers/slack/tests/unit/slack/operators/test_slack.py
index 485f52a8d63..c2d178bf6c1 100644
--- a/providers/slack/tests/unit/slack/operators/test_slack.py
+++ b/providers/slack/tests/unit/slack/operators/test_slack.py
@@ -200,6 +200,24 @@ class TestSlackAPIPostOperator:
},
)
+ @mock.patch("airflow.providers.slack.operators.slack.SlackHook")
+ def test_execute_returns_api_response(self, mock_hook):
+ """Test that execute returns Slack API response data for XCom."""
+ mock_response = mock.MagicMock()
+ mock_response.data = {"ok": True, "ts": "1234567890.123456",
"channel": "C1234567890"}
+ mock_hook.return_value.call.return_value = mock_response
+
+ op = SlackAPIPostOperator(
+ task_id="slack",
+ slack_conn_id=SLACK_API_TEST_CONNECTION_ID,
+ channel=self.test_channel,
+ text=self.test_text,
+ )
+ result = op.execute({})
+
+ assert result == {"ok": True, "ts": "1234567890.123456", "channel":
"C1234567890"}
+ assert result["ts"] == "1234567890.123456"
+
class TestSlackAPIFileOperator:
def setup_method(self):
@@ -368,3 +386,35 @@ class TestSlackAPIFileOperator:
snippet_type=None,
thread_ts="1234567890.123456",
)
+
+ def test_execute_returns_api_response(self):
+ """Test that execute returns Slack API response data for XCom."""
+ mock_response = [mock.MagicMock()]
+ mock_response[0].data = {
+ "ok": True,
+ "ts": "1234567890.123456",
+ "channel": "C1234567890",
+ "file": {"id": "F1234567890"},
+ }
+
+ op = SlackAPIFileOperator(
+ task_id="slack",
+ slack_conn_id=SLACK_API_TEST_CONNECTION_ID,
+ channels="#test-channel",
+ content="test-content",
+ )
+ with mock.patch(
+
"airflow.providers.slack.operators.slack.SlackHook.send_file_v1_to_v2",
+ return_value=mock_response,
+ ):
+ result = op.execute({})
+
+ expected = {
+ "ok": True,
+ "ts": "1234567890.123456",
+ "channel": "C1234567890",
+ "file": {"id": "F1234567890"},
+ }
+ assert result == [expected]
+ assert result[0]["ts"] == "1234567890.123456"
+ assert result[0]["file"]["id"] == "F1234567890"