betodealmeida commented on a change in pull request #16375:
URL: https://github.com/apache/superset/pull/16375#discussion_r695243414



##########
File path: superset/reports/dao.py
##########
@@ -113,6 +113,24 @@ def bulk_delete(
                 db.session.rollback()
             raise DAODeleteFailedError(str(ex)) from ex
 
+    @staticmethod
+    def validate_unique_creation_method(
+        user_id: int, dashboard_id: Optional[int] = None, chart_id: 
Optional[int] = None
+    ) -> bool:
+        """
+        Validate if the user already has a chart or dashboard
+        with a report attached form the self subscribe reports
+        """
+
+        query = 
db.session.query(ReportSchedule).filter_by(created_by_fk=user_id)
+        if query is not None and dashboard_id is not None:

Review comment:
       `query` can't be `None`, since it's defined as something else in line 
125.
   
   ```suggestion
           if dashboard_id is not None:
   ```

##########
File path: superset/reports/commands/create.py
##########
@@ -59,7 +60,10 @@ def validate(self) -> None:
         owner_ids: Optional[List[int]] = self._properties.get("owners")
         name = self._properties.get("name", "")
         report_type = self._properties.get("type")
-
+        creation_method = self._properties.get("creation_method")
+        chart_id = self._properties.get("chart")
+        dashboard_id = self._properties.get("dashboard")
+        user_id = self._actor.id

Review comment:
       Nit: leave the empty line separating variable definitions from the test 
below.
   
   ```suggestion
           user_id = self._actor.id
           
   ```

##########
File path: superset/reports/dao.py
##########
@@ -113,6 +113,24 @@ def bulk_delete(
                 db.session.rollback()
             raise DAODeleteFailedError(str(ex)) from ex
 
+    @staticmethod
+    def validate_unique_creation_method(
+        user_id: int, dashboard_id: Optional[int] = None, chart_id: 
Optional[int] = None
+    ) -> bool:
+        """
+        Validate if the user already has a chart or dashboard
+        with a report attached form the self subscribe reports
+        """
+
+        query = 
db.session.query(ReportSchedule).filter_by(created_by_fk=user_id)
+        if query is not None and dashboard_id is not None:
+            query = query.filter(ReportSchedule.dashboard_id == dashboard_id)
+
+        if query is not None and chart_id is not None:

Review comment:
       Same here:
   
   ```suggestion
           if chart_id is not None:
   ```

##########
File path: superset/reports/commands/exceptions.py
##########
@@ -167,6 +167,19 @@ def __init__(self) -> None:
         super().__init__([_("Name must be unique")], field_name="name")
 
 
+class ReportScheduleCreationMethodUniquenessValidationError(ValidationError):
+    """
+    Marshmallow validation error for Report Schedule with creation method 
charts
+    or dashboards already existing on a report.
+    """
+
+    def __init__(self) -> None:
+        super().__init__(

Review comment:
       ```suggestion
           status = 409
           
           super().__init__(
   ```

##########
File path: superset/reports/dao.py
##########
@@ -113,6 +113,24 @@ def bulk_delete(
                 db.session.rollback()
             raise DAODeleteFailedError(str(ex)) from ex
 
+    @staticmethod
+    def validate_unique_creation_method(
+        user_id: int, dashboard_id: Optional[int] = None, chart_id: 
Optional[int] = None
+    ) -> bool:
+        """
+        Validate if the user already has a chart or dashboard
+        with a report attached form the self subscribe reports
+        """
+
+        query = 
db.session.query(ReportSchedule).filter_by(created_by_fk=user_id)
+        if query is not None and dashboard_id is not None:
+            query = query.filter(ReportSchedule.dashboard_id == dashboard_id)
+
+        if query is not None and chart_id is not None:
+            query = query.filter(ReportSchedule.chart_id == chart_id)
+        print(query)
+        return not db.session.query(query.exists()).scalar()

Review comment:
       Leave an empty line to logically separate the return from the previous 
test.
   
   ```suggestion
   
           return not db.session.query(query.exists()).scalar()
   ```

##########
File path: tests/integration_tests/reports/api_tests.py
##########
@@ -790,6 +790,96 @@ def test_no_dashboard_report_schedule_schema(self):
             == "Please save your dashboard first, then try creating a new 
email report."
         )
 
+    @pytest.mark.usefixtures(
+        "load_birth_names_dashboard_with_slices", "create_report_schedules"
+    )
+    def test_create_multiple_creation_method_report_schedule_charts(self):
+        """
+        ReportSchedule Api: Test create multiple reports with the same 
creation method
+        """
+        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": "name4",
+            "description": "description",
+            "creation_method": ReportCreationMethodType.CHARTS,
+            "crontab": "0 9 * * *",
+            "working_timeout": 3600,
+            "chart": chart.id,
+        }
+        uri = "api/v1/report/"
+        rv = self.client.post(uri, json=report_schedule_data)
+        data = json.loads(rv.data.decode("utf-8"))
+        print(data)
+        assert rv.status_code == 201
+
+        # this second time it should receive an error because the chart has an 
attached report
+        # with the same creation method from the same user.
+        report_schedule_data = {
+            "type": ReportScheduleType.REPORT,
+            "name": "name5",
+            "description": "description",
+            "creation_method": ReportCreationMethodType.CHARTS,
+            "crontab": "0 9 * * *",
+            "working_timeout": 3600,
+            "chart": chart.id,
+        }
+        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"]["creation_method"] == [
+            "Resource already has an attached report."
+        ]
+
+    @pytest.mark.usefixtures(
+        "load_birth_names_dashboard_with_slices", "create_report_schedules"
+    )
+    def test_create_multiple_creation_method_report_schedule_dashboards(self):
+        """
+        ReportSchedule Api: Test create multiple reports with the same 
creation method
+        """
+        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": "name4",
+            "description": "description",
+            "creation_method": ReportCreationMethodType.DASHBOARDS,
+            "crontab": "0 9 * * *",
+            "working_timeout": 3600,
+            "dashboard": dashboard.id,
+        }
+        uri = "api/v1/report/"
+        rv = self.client.post(uri, json=report_schedule_data)
+        data = json.loads(rv.data.decode("utf-8"))
+        print(data)

Review comment:
       ```suggestion
   ```

##########
File path: tests/integration_tests/reports/api_tests.py
##########
@@ -790,6 +790,96 @@ def test_no_dashboard_report_schedule_schema(self):
             == "Please save your dashboard first, then try creating a new 
email report."
         )
 
+    @pytest.mark.usefixtures(
+        "load_birth_names_dashboard_with_slices", "create_report_schedules"
+    )
+    def test_create_multiple_creation_method_report_schedule_charts(self):
+        """
+        ReportSchedule Api: Test create multiple reports with the same 
creation method
+        """
+        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": "name4",
+            "description": "description",
+            "creation_method": ReportCreationMethodType.CHARTS,
+            "crontab": "0 9 * * *",
+            "working_timeout": 3600,
+            "chart": chart.id,
+        }
+        uri = "api/v1/report/"
+        rv = self.client.post(uri, json=report_schedule_data)
+        data = json.loads(rv.data.decode("utf-8"))
+        print(data)
+        assert rv.status_code == 201
+
+        # this second time it should receive an error because the chart has an 
attached report
+        # with the same creation method from the same user.
+        report_schedule_data = {
+            "type": ReportScheduleType.REPORT,
+            "name": "name5",
+            "description": "description",
+            "creation_method": ReportCreationMethodType.CHARTS,
+            "crontab": "0 9 * * *",
+            "working_timeout": 3600,
+            "chart": chart.id,
+        }
+        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

Review comment:
       I think returning a 409 
(https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409) makes more sense 
here. All you need to do is to set the attribute `status = 409` in your new 
exception.

##########
File path: tests/integration_tests/reports/api_tests.py
##########
@@ -790,6 +790,96 @@ def test_no_dashboard_report_schedule_schema(self):
             == "Please save your dashboard first, then try creating a new 
email report."
         )
 
+    @pytest.mark.usefixtures(
+        "load_birth_names_dashboard_with_slices", "create_report_schedules"
+    )
+    def test_create_multiple_creation_method_report_schedule_charts(self):
+        """
+        ReportSchedule Api: Test create multiple reports with the same 
creation method
+        """
+        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": "name4",
+            "description": "description",
+            "creation_method": ReportCreationMethodType.CHARTS,
+            "crontab": "0 9 * * *",
+            "working_timeout": 3600,
+            "chart": chart.id,
+        }
+        uri = "api/v1/report/"
+        rv = self.client.post(uri, json=report_schedule_data)
+        data = json.loads(rv.data.decode("utf-8"))
+        print(data)

Review comment:
       ```suggestion
   ```

##########
File path: superset/reports/dao.py
##########
@@ -113,6 +113,24 @@ def bulk_delete(
                 db.session.rollback()
             raise DAODeleteFailedError(str(ex)) from ex
 
+    @staticmethod
+    def validate_unique_creation_method(
+        user_id: int, dashboard_id: Optional[int] = None, chart_id: 
Optional[int] = None
+    ) -> bool:
+        """
+        Validate if the user already has a chart or dashboard
+        with a report attached form the self subscribe reports
+        """
+
+        query = 
db.session.query(ReportSchedule).filter_by(created_by_fk=user_id)
+        if query is not None and dashboard_id is not None:
+            query = query.filter(ReportSchedule.dashboard_id == dashboard_id)
+
+        if query is not None and chart_id is not None:
+            query = query.filter(ReportSchedule.chart_id == chart_id)
+        print(query)

Review comment:
       ```suggestion
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to