feat: add logic to creation_method for reports schedule (#15685)

* migration

* added logic for creation_method

* revisions

* added index

* Update superset/migrations/versions/3317e9248280_add_creation_method_to_reports_model.py

* filters

* Update superset/models/reports.py

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* Update superset/reports/schemas.py

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* Update tests/integration_tests/reports/api_tests.py

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* revisions

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
This commit is contained in:
AAfghahi 2021-07-15 21:27:54 -04:00 committed by GitHub
parent 6b790990a8
commit 674f234de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 129 additions and 1 deletions

View File

@ -74,6 +74,12 @@ class ReportDataFormat(str, enum.Enum):
DATA = "CSV"
class ReportCreationMethodType(str, enum.Enum):
CHARTS = "charts"
DASHBOARDS = "dashboards"
ALERTS_REPORTS = "alerts_reports"
report_schedule_user = Table(
"report_schedule_user",
metadata,
@ -102,6 +108,9 @@ class ReportSchedule(Model, AuditMixinNullable):
context_markdown = Column(Text)
active = Column(Boolean, default=True, index=True)
crontab = Column(String(1000), nullable=False)
creation_method = Column(
String(255), server_default=ReportCreationMethodType.ALERTS_REPORTS
)
report_format = Column(String(50), default=ReportDataFormat.VISUALIZATION)
sql = Column(Text())
# (Alerts/Reports) M-O to chart

View File

@ -84,6 +84,7 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
"chart.id",
"chart.slice_name",
"context_markdown",
"creation_method",
"crontab",
"dashboard.dashboard_title",
"dashboard.id",
@ -123,6 +124,7 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
"created_by.first_name",
"created_by.last_name",
"created_on",
"creation_method",
"crontab",
"crontab_humanized",
"id",
@ -140,6 +142,7 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
"active",
"chart",
"context_markdown",
"creation_method",
"crontab",
"dashboard",
"database",
@ -173,7 +176,13 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
"type",
"crontab_humanized",
]
search_columns = ["name", "active", "created_by", "type", "last_state"]
search_columns = [
"name",
"active",
"created_by",
"type",
"last_state",
]
search_filters = {"name": [ReportScheduleAllTextFilter]}
allowed_rel_fields = {"owners", "chart", "dashboard", "database", "created_by"}
filter_rel_fields = {

View File

@ -20,8 +20,10 @@ from croniter import croniter
from flask_babel import gettext as _
from marshmallow import fields, Schema, validate, validates_schema
from marshmallow.validate import Length, Range, ValidationError
from marshmallow_enum import EnumField
from superset.models.reports import (
ReportCreationMethodType,
ReportDataFormat,
ReportRecipientType,
ReportScheduleType,
@ -83,6 +85,10 @@ working_timeout_description = (
"If an alert is staled at a working state, how long until it's state is reseted to"
" error"
)
creation_method_description = (
"Creation method is used to inform the frontend whether the report/alert was "
"created in the dashboard, chart, or alerts and reports UI."
)
def validate_crontab(value: Union[bytes, bytearray, str]) -> None:
@ -150,6 +156,12 @@ class ReportSchedulePostSchema(Schema):
description=sql_description, example="SELECT value FROM time_series_table"
)
chart = fields.Integer(required=False, allow_none=True)
creation_method = EnumField(
ReportCreationMethodType,
by_value=True,
required=True,
description=creation_method_description,
)
dashboard = fields.Integer(required=False, allow_none=True)
database = fields.Integer(required=False)
owners = fields.List(fields.Integer(description=owners_description))
@ -226,6 +238,12 @@ class ReportSchedulePutSchema(Schema):
allow_none=True,
)
chart = fields.Integer(required=False, allow_none=True)
creation_method = EnumField(
ReportCreationMethodType,
by_value=True,
allow_none=True,
description=creation_method_description,
)
dashboard = fields.Integer(required=False, allow_none=True)
database = fields.Integer(required=False)
owners = fields.List(fields.Integer(description=owners_description), required=False)

View File

@ -29,6 +29,7 @@ from superset.models.slice import Slice
from superset.models.dashboard import Dashboard
from superset.models.reports import (
ReportSchedule,
ReportCreationMethodType,
ReportRecipients,
ReportExecutionLog,
ReportScheduleType,
@ -269,6 +270,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"changed_on_delta_humanized",
"created_by",
"created_on",
"creation_method",
"crontab",
"crontab_humanized",
"id",
@ -442,6 +444,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"name": "new3",
"description": "description",
"crontab": "0 9 * * *",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"recipients": [
{
"type": ReportRecipientType.EMAIL,
@ -470,6 +473,7 @@ class TestReportSchedulesApi(SupersetTestCase):
assert created_model.crontab == report_schedule_data["crontab"]
assert created_model.chart.id == report_schedule_data["chart"]
assert created_model.database.id == report_schedule_data["database"]
assert created_model.creation_method == report_schedule_data["creation_method"]
# Rollback changes
db.session.delete(created_model)
db.session.commit()
@ -487,6 +491,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "name3",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"chart": chart.id,
"database": example_db.id,
@ -503,6 +508,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"name": "name3",
"description": "description",
"crontab": "0 9 * * *",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"chart": chart.id,
}
uri = "api/v1/report/"
@ -532,6 +538,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.REPORT,
"name": "name3",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"chart": chart.id,
"database": example_db.id,
@ -545,6 +552,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "new3",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"recipients": [
{
@ -569,6 +577,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "new3",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"recipients": [
{
@ -592,6 +601,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "new3",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"recipients": [
{
@ -617,6 +627,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "new4",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"recipients": [
{
@ -642,6 +653,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "new5",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"recipients": [
{
@ -678,6 +690,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"name": "new3",
"description": "description",
"crontab": "0 9 * * *",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"chart": chart.id,
"dashboard": dashboard.id,
"database": example_db.id,
@ -701,6 +714,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "new3",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"chart": chart.id,
}
@ -726,6 +740,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"type": ReportScheduleType.ALERT,
"name": "new3",
"description": "description",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"crontab": "0 9 * * *",
"chart": chart_max_id + 1,
"database": database_max_id + 1,
@ -748,6 +763,7 @@ class TestReportSchedulesApi(SupersetTestCase):
"name": "new3",
"description": "description",
"crontab": "0 9 * * *",
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
"dashboard": dashboard_max_id + 1,
"database": examples_db.id,
}
@ -757,6 +773,82 @@ class TestReportSchedulesApi(SupersetTestCase):
data = json.loads(rv.data.decode("utf-8"))
assert data == {"message": {"dashboard": "Dashboard does not exist"}}
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
# TODO (AAfghahi): I am going to enable this when the report schedule feature is fully finished
# def test_create_report_schedule_no_creation_method(self):
# """
# ReportSchedule Api: Test create report schedule
# """
# self.login(username="admin")
# chart = db.session.query(Slice).first()
# example_db = get_example_database()
# report_schedule_data = {
# "type": ReportScheduleType.ALERT,
# "name": "new3",
# "description": "description",
# "crontab": "0 9 * * *",
# "recipients": [
# {
# "type": ReportRecipientType.EMAIL,
# "recipient_config_json": {"target": "target@superset.org"},
# },
# {
# "type": ReportRecipientType.SLACK,
# "recipient_config_json": {"target": "channel"},
# },
# ],
# "grace_period": 14400,
# "working_timeout": 3600,
# "chart": chart.id,
# "database": example_db.id,
# }
# uri = "api/v1/report/"
# rv = self.client.post(uri, json=report_schedule_data)
# response = json.loads(rv.data.decode("utf-8"))
# assert response == {
# "message": {"creation_method": ["Missing data for required field."]}
# }
# assert rv.status_code == 400
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
def test_create_report_schedule_invalid_creation_method(self):
"""
ReportSchedule API: Test create report schedule
"""
self.login(username="admin")
chart = db.session.query(Slice).first()
example_db = get_example_database()
report_schedule_data = {
"type": ReportScheduleType.ALERT,
"name": "new3",
"description": "description",
"creation_method": "BAD_CREATION_METHOD",
"crontab": "0 9 * * *",
"recipients": [
{
"type": ReportRecipientType.EMAIL,
"recipient_config_json": {"target": "target@superset.org"},
},
{
"type": ReportRecipientType.SLACK,
"recipient_config_json": {"target": "channel"},
},
],
"grace_period": 14400,
"working_timeout": 3600,
"chart": chart.id,
"database": example_db.id,
}
uri = "api/v1/report/"
rv = self.client.post(uri, json=report_schedule_data)
response = json.loads(rv.data.decode("utf-8"))
assert response == {
"message": {"creation_method": ["Invalid enum value BAD_CREATION_METHOD"]}
}
assert rv.status_code == 400
@pytest.mark.usefixtures("create_report_schedules")
def test_update_report_schedule(self):
"""