diff --git a/superset-frontend/src/reports/actions/reports.js b/superset-frontend/src/reports/actions/reports.js index c7eb0c934561f..55cea9dbaa7c9 100644 --- a/superset-frontend/src/reports/actions/reports.js +++ b/superset-frontend/src/reports/actions/reports.js @@ -111,7 +111,7 @@ export const addReport = report => dispatch => { const parsedError = await getClientErrorObject(e); const errorMessage = parsedError.message; const errorArr = Object.keys(errorMessage); - const error = errorMessage[errorArr[0]][0]; + const error = errorMessage[errorArr[0]]; dispatch( addDangerToast( t('An error occurred while editing this report: %s', error), diff --git a/superset/reports/commands/base.py b/superset/reports/commands/base.py index bb4064d22cde7..3582767ef65f2 100644 --- a/superset/reports/commands/base.py +++ b/superset/reports/commands/base.py @@ -22,9 +22,12 @@ from superset.charts.dao import ChartDAO from superset.commands.base import BaseCommand from superset.dashboards.dao import DashboardDAO +from superset.models.reports import ReportCreationMethodType from superset.reports.commands.exceptions import ( ChartNotFoundValidationError, + ChartNotSavedValidationError, DashboardNotFoundValidationError, + DashboardNotSavedValidationError, ReportScheduleChartOrDashboardValidationError, ) @@ -47,6 +50,17 @@ def validate_chart_dashboard( """ Validate chart or dashboard relation """ chart_id = self._properties.get("chart") dashboard_id = self._properties.get("dashboard") + creation_method = self._properties.get("creation_method") + + if creation_method == ReportCreationMethodType.CHARTS and not chart_id: + # User has not saved chart yet in Explore view + exceptions.append(ChartNotSavedValidationError()) + return + + if creation_method == ReportCreationMethodType.DASHBOARDS and not dashboard_id: + exceptions.append(DashboardNotSavedValidationError()) + return + if chart_id and dashboard_id: exceptions.append(ReportScheduleChartOrDashboardValidationError()) if chart_id: diff --git a/superset/reports/commands/exceptions.py b/superset/reports/commands/exceptions.py index dfe2402da0449..b7d7b433ba952 100644 --- a/superset/reports/commands/exceptions.py +++ b/superset/reports/commands/exceptions.py @@ -80,6 +80,32 @@ def __init__(self) -> None: super().__init__(_("Choose a chart or dashboard not both"), field_name="chart") +class ChartNotSavedValidationError(ValidationError): + """ + Marshmallow validation error for charts that haven't been saved yet + """ + + def __init__(self) -> None: + super().__init__( + _("Please save your chart first, then try creating a new email report."), + field_name="chart", + ) + + +class DashboardNotSavedValidationError(ValidationError): + """ + Marshmallow validation error for dashboards that haven't been saved yet + """ + + def __init__(self) -> None: + super().__init__( + _( + "Please save your dashboard first, then try creating a new email report." + ), + field_name="dashboard", + ) + + class ReportScheduleInvalidError(CommandInvalidError): message = _("Report Schedule parameters are invalid.") diff --git a/tests/integration_tests/reports/api_tests.py b/tests/integration_tests/reports/api_tests.py index 3c306490d1389..55fce65162ea9 100644 --- a/tests/integration_tests/reports/api_tests.py +++ b/tests/integration_tests/reports/api_tests.py @@ -734,6 +734,62 @@ def test_create_report_schedule_schema(self): assert data["result"]["timezone"] == "America/Los_Angeles" assert rv.status_code == 201 + @pytest.mark.usefixtures( + "load_birth_names_dashboard_with_slices", "create_report_schedules" + ) + def test_unsaved_report_schedule_schema(self): + """ + ReportSchedule Api: Test create report schedule with unsaved chart + """ + self.login(username="admin") + chart = db.session.query(Slice).first() + dashboard = db.session.query(Dashboard).first() + example_db = get_example_database() + + report_schedule_data = { + "type": ReportScheduleType.REPORT, + "name": "name3", + "description": "description", + "creation_method": ReportCreationMethodType.CHARTS, + "crontab": "0 9 * * *", + "chart": 0, + } + uri = "api/v1/report/" + rv = self.client.post(uri, json=report_schedule_data) + data = json.loads(rv.data.decode("utf-8")) + assert rv.status_code == 422 + assert ( + data["message"]["chart"] + == "Please save your chart first, then try creating a new email report." + ) + + @pytest.mark.usefixtures( + "load_birth_names_dashboard_with_slices", "create_report_schedules" + ) + def test_no_dashboard_report_schedule_schema(self): + """ + ReportSchedule Api: Test create report schedule with not dashboard id + """ + self.login(username="admin") + chart = db.session.query(Slice).first() + dashboard = db.session.query(Dashboard).first() + example_db = get_example_database() + report_schedule_data = { + "type": ReportScheduleType.REPORT, + "name": "name3", + "description": "description", + "creation_method": ReportCreationMethodType.DASHBOARDS, + "crontab": "0 9 * * *", + } + uri = "api/v1/report/" + rv = self.client.post(uri, json=report_schedule_data) + data = json.loads(rv.data.decode("utf-8")) + assert rv.status_code == 422 + assert ( + data["message"]["dashboard"] + == "Please save your dashboard first, then try creating a new email report." + ) + @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices") def test_create_report_schedule_chart_dash_validation(self): """