diff --git a/superset/commands/report/execute.py b/superset/commands/report/execute.py index 3ec5bdfa97b22..b14dfa0027e0f 100644 --- a/superset/commands/report/execute.py +++ b/superset/commands/report/execute.py @@ -367,6 +367,7 @@ def _get_log_data(self) -> HeaderDataType: chart_id = None dashboard_id = None report_source = None + slack_channels = None if self._report_schedule.chart: report_source = ReportSourceFormat.CHART chart_id = self._report_schedule.chart_id @@ -374,6 +375,14 @@ def _get_log_data(self) -> HeaderDataType: report_source = ReportSourceFormat.DASHBOARD dashboard_id = self._report_schedule.dashboard_id + if self._report_schedule.recipients: + slack_channels = [ + recipient.recipient_config_json + for recipient in self._report_schedule.recipients + if recipient.type + in [ReportRecipientType.SLACK, ReportRecipientType.SLACKV2] + ] + log_data: HeaderDataType = { "notification_type": self._report_schedule.type, "notification_source": report_source, @@ -381,6 +390,7 @@ def _get_log_data(self) -> HeaderDataType: "chart_id": chart_id, "dashboard_id": dashboard_id, "owners": self._report_schedule.owners, + "slack_channels": slack_channels, } return log_data diff --git a/superset/utils/core.py b/superset/utils/core.py index 6a85521388f07..9480e5473330d 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -167,6 +167,7 @@ class HeaderDataType(TypedDict): notification_source: str | None chart_id: int | None dashboard_id: int | None + slack_channels: list[str] | None class DatasourceDict(TypedDict): diff --git a/tests/unit_tests/commands/report/execute_test.py b/tests/unit_tests/commands/report/execute_test.py new file mode 100644 index 0000000000000..6963d7c0debc7 --- /dev/null +++ b/tests/unit_tests/commands/report/execute_test.py @@ -0,0 +1,291 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from superset.commands.report.execute import BaseReportState +from superset.reports.models import ( + ReportRecipientType, + ReportSchedule, + ReportSourceFormat, +) +from superset.utils.core import HeaderDataType + +# Returns correct log data when report schedule has a chart +# def test_log_data_with_chart( mocker): + +# # Mocking the report schedule +# mock_report_schedule = mocker.Mock(spec=ReportSchedule) +# mock_report_schedule.chart = True +# mock_report_schedule.chart_id = 123 +# mock_report_schedule.dashboard_id = None +# mock_report_schedule.type = "report_type" +# mock_report_schedule.report_format = "report_format" +# mock_report_schedule.owners = [1, 2] +# mock_report_schedule.recipients = [] + + +# # Initializing the class and setting the report schedule +# class_instance = BaseReportState(mock_report_schedule, 'January 1, 2021', 'execution_id_example') +# class_instance._report_schedule = mock_report_schedule + +# # Calling the method +# result = class_instance._get_log_data(), + +# # Expected result +# expected_result: HeaderDataType = { +# "notification_type": "report_type", +# "notification_source": ReportSourceFormat.CHART, +# "notification_format": "report_format", +# "chart_id": 123, +# "dashboard_id": None, +# "owners": [1, 2], +# "slack_channels": None, +# } +# # Assertions +# assert result == expected_result +# mock_report_schedule.recipients = [ +# mocker.Mock(type=ReportRecipientType.SLACK, recipient_config_json="channel_1"), +# mocker.Mock(type=ReportRecipientType.SLACKV2, recipient_config_json="channel_2") +# ] + + +# Returns correct log data when report schedule has no chart or dashboard +def test_log_data_with_chart(mocker): + # Mocking the report schedule + mock_report_schedule = mocker.Mock(spec=ReportSchedule) + mock_report_schedule.chart = True + mock_report_schedule.chart_id = 123 + mock_report_schedule.dashboard_id = None + mock_report_schedule.type = "report_type" + mock_report_schedule.report_format = "report_format" + mock_report_schedule.owners = [1, 2] + mock_report_schedule.recipients = [] + + # Initializing the class and setting the report schedule + class_instance = BaseReportState( + mock_report_schedule, "January 1, 2021", "execution_id_example" + ) + class_instance._report_schedule = mock_report_schedule + + # Calling the method + result = class_instance._get_log_data() + + # Expected result + expected_result: HeaderDataType = { + "notification_type": "report_type", + "notification_source": ReportSourceFormat.CHART, + "notification_format": "report_format", + "chart_id": 123, + "dashboard_id": None, + "owners": [1, 2], + "slack_channels": None, + } + + # Assertions + assert result == expected_result + + +def test_log_data_with_dashboard(mocker): + # Mocking the report schedule + mock_report_schedule = mocker.Mock(spec=ReportSchedule) + mock_report_schedule.chart = False + mock_report_schedule.chart_id = None + mock_report_schedule.dashboard_id = 123 + mock_report_schedule.type = "report_type" + mock_report_schedule.report_format = "report_format" + mock_report_schedule.owners = [1, 2] + mock_report_schedule.recipients = [] + + # Initializing the class and setting the report schedule + class_instance = BaseReportState( + mock_report_schedule, "January 1, 2021", "execution_id_example" + ) + class_instance._report_schedule = mock_report_schedule + + # Calling the method + result = class_instance._get_log_data() + + # Expected result + expected_result: HeaderDataType = { + "notification_type": "report_type", + "notification_source": ReportSourceFormat.DASHBOARD, + "notification_format": "report_format", + "chart_id": None, + "dashboard_id": 123, + "owners": [1, 2], + "slack_channels": None, + } + + # Assertions + assert result == expected_result + + +def test_log_data_with_email_recipients(mocker): + # Mocking the report schedule + mock_report_schedule = mocker.Mock(spec=ReportSchedule) + mock_report_schedule.chart = False + mock_report_schedule.chart_id = None + mock_report_schedule.dashboard_id = 123 + mock_report_schedule.type = "report_type" + mock_report_schedule.report_format = "report_format" + mock_report_schedule.owners = [1, 2] + mock_report_schedule.recipients = [] + mock_report_schedule.recipients = [ + mocker.Mock(type=ReportRecipientType.EMAIL, recipient_config_json="email_1"), + mocker.Mock(type=ReportRecipientType.EMAIL, recipient_config_json="email_2"), + ] + + # Initializing the class and setting the report schedule + class_instance = BaseReportState( + mock_report_schedule, "January 1, 2021", "execution_id_example" + ) + class_instance._report_schedule = mock_report_schedule + + # Calling the method + result = class_instance._get_log_data() + + # Expected result + expected_result: HeaderDataType = { + "notification_type": "report_type", + "notification_source": ReportSourceFormat.DASHBOARD, + "notification_format": "report_format", + "chart_id": None, + "dashboard_id": 123, + "owners": [1, 2], + "slack_channels": [], + } + + # Assertions + assert result == expected_result + + +def test_log_data_with_slack_recipients(mocker): + # Mocking the report schedule + mock_report_schedule = mocker.Mock(spec=ReportSchedule) + mock_report_schedule.chart = False + mock_report_schedule.chart_id = None + mock_report_schedule.dashboard_id = 123 + mock_report_schedule.type = "report_type" + mock_report_schedule.report_format = "report_format" + mock_report_schedule.owners = [1, 2] + mock_report_schedule.recipients = [] + mock_report_schedule.recipients = [ + mocker.Mock(type=ReportRecipientType.SLACK, recipient_config_json="channel_1"), + mocker.Mock(type=ReportRecipientType.SLACK, recipient_config_json="channel_2"), + ] + + # Initializing the class and setting the report schedule + class_instance = BaseReportState( + mock_report_schedule, "January 1, 2021", "execution_id_example" + ) + class_instance._report_schedule = mock_report_schedule + + # Calling the method + result = class_instance._get_log_data() + + # Expected result + expected_result: HeaderDataType = { + "notification_type": "report_type", + "notification_source": ReportSourceFormat.DASHBOARD, + "notification_format": "report_format", + "chart_id": None, + "dashboard_id": 123, + "owners": [1, 2], + "slack_channels": ["channel_1", "channel_2"], + } + + # Assertions + assert result == expected_result + + +# Returns correct log data when report schedule has no owners +def test_log_data_no_owners(mocker): + # Mocking the report schedule + mock_report_schedule = mocker.Mock(spec=ReportSchedule) + mock_report_schedule.chart = False + mock_report_schedule.chart_id = None + mock_report_schedule.dashboard_id = 123 + mock_report_schedule.type = "report_type" + mock_report_schedule.report_format = "report_format" + mock_report_schedule.owners = [] + mock_report_schedule.recipients = [ + mocker.Mock(type=ReportRecipientType.SLACK, recipient_config_json="channel_1"), + mocker.Mock(type=ReportRecipientType.SLACK, recipient_config_json="channel_2"), + ] + + # Initializing the class and setting the report schedule + class_instance = BaseReportState( + mock_report_schedule, "January 1, 2021", "execution_id_example" + ) + class_instance._report_schedule = mock_report_schedule + + # Calling the method + result = class_instance._get_log_data() + + # Expected result + expected_result: HeaderDataType = { + "notification_type": "report_type", + "notification_source": ReportSourceFormat.DASHBOARD, + "notification_format": "report_format", + "chart_id": None, + "dashboard_id": 123, + "owners": [], + "slack_channels": ["channel_1", "channel_2"], + } + + # Assertions + assert result == expected_result + + +# Handles missing or null values in report schedule attributes gracefully +def test_log_data_with_missing_values(mocker): + # Mocking the report schedule + mock_report_schedule = mocker.Mock(spec=ReportSchedule) + mock_report_schedule.chart = None + mock_report_schedule.chart_id = None + mock_report_schedule.dashboard_id = None + mock_report_schedule.type = "report_type" + mock_report_schedule.report_format = "report_format" + mock_report_schedule.owners = [1, 2] + mock_report_schedule.recipients = [ + mocker.Mock(type=ReportRecipientType.SLACK, recipient_config_json="channel_1"), + mocker.Mock( + type=ReportRecipientType.SLACKV2, recipient_config_json="channel_2" + ), + ] + + # Initializing the class and setting the report schedule + class_instance = BaseReportState( + mock_report_schedule, "January 1, 2021", "execution_id_example" + ) + class_instance._report_schedule = mock_report_schedule + + # Calling the method + result = class_instance._get_log_data() + + # Expected result + expected_result: HeaderDataType = { + "notification_type": "report_type", + "notification_source": ReportSourceFormat.DASHBOARD, + "notification_format": "report_format", + "chart_id": None, + "dashboard_id": None, + "owners": [1, 2], + "slack_channels": ["channel_1", "channel_2"], + } + + # Assertions + assert result == expected_result