mirror of https://github.com/apache/superset.git
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:
parent
6b790990a8
commit
674f234de6
|
@ -74,6 +74,12 @@ class ReportDataFormat(str, enum.Enum):
|
||||||
DATA = "CSV"
|
DATA = "CSV"
|
||||||
|
|
||||||
|
|
||||||
|
class ReportCreationMethodType(str, enum.Enum):
|
||||||
|
CHARTS = "charts"
|
||||||
|
DASHBOARDS = "dashboards"
|
||||||
|
ALERTS_REPORTS = "alerts_reports"
|
||||||
|
|
||||||
|
|
||||||
report_schedule_user = Table(
|
report_schedule_user = Table(
|
||||||
"report_schedule_user",
|
"report_schedule_user",
|
||||||
metadata,
|
metadata,
|
||||||
|
@ -102,6 +108,9 @@ class ReportSchedule(Model, AuditMixinNullable):
|
||||||
context_markdown = Column(Text)
|
context_markdown = Column(Text)
|
||||||
active = Column(Boolean, default=True, index=True)
|
active = Column(Boolean, default=True, index=True)
|
||||||
crontab = Column(String(1000), nullable=False)
|
crontab = Column(String(1000), nullable=False)
|
||||||
|
creation_method = Column(
|
||||||
|
String(255), server_default=ReportCreationMethodType.ALERTS_REPORTS
|
||||||
|
)
|
||||||
report_format = Column(String(50), default=ReportDataFormat.VISUALIZATION)
|
report_format = Column(String(50), default=ReportDataFormat.VISUALIZATION)
|
||||||
sql = Column(Text())
|
sql = Column(Text())
|
||||||
# (Alerts/Reports) M-O to chart
|
# (Alerts/Reports) M-O to chart
|
||||||
|
|
|
@ -84,6 +84,7 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
|
||||||
"chart.id",
|
"chart.id",
|
||||||
"chart.slice_name",
|
"chart.slice_name",
|
||||||
"context_markdown",
|
"context_markdown",
|
||||||
|
"creation_method",
|
||||||
"crontab",
|
"crontab",
|
||||||
"dashboard.dashboard_title",
|
"dashboard.dashboard_title",
|
||||||
"dashboard.id",
|
"dashboard.id",
|
||||||
|
@ -123,6 +124,7 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
|
||||||
"created_by.first_name",
|
"created_by.first_name",
|
||||||
"created_by.last_name",
|
"created_by.last_name",
|
||||||
"created_on",
|
"created_on",
|
||||||
|
"creation_method",
|
||||||
"crontab",
|
"crontab",
|
||||||
"crontab_humanized",
|
"crontab_humanized",
|
||||||
"id",
|
"id",
|
||||||
|
@ -140,6 +142,7 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
|
||||||
"active",
|
"active",
|
||||||
"chart",
|
"chart",
|
||||||
"context_markdown",
|
"context_markdown",
|
||||||
|
"creation_method",
|
||||||
"crontab",
|
"crontab",
|
||||||
"dashboard",
|
"dashboard",
|
||||||
"database",
|
"database",
|
||||||
|
@ -173,7 +176,13 @@ class ReportScheduleRestApi(BaseSupersetModelRestApi):
|
||||||
"type",
|
"type",
|
||||||
"crontab_humanized",
|
"crontab_humanized",
|
||||||
]
|
]
|
||||||
search_columns = ["name", "active", "created_by", "type", "last_state"]
|
search_columns = [
|
||||||
|
"name",
|
||||||
|
"active",
|
||||||
|
"created_by",
|
||||||
|
"type",
|
||||||
|
"last_state",
|
||||||
|
]
|
||||||
search_filters = {"name": [ReportScheduleAllTextFilter]}
|
search_filters = {"name": [ReportScheduleAllTextFilter]}
|
||||||
allowed_rel_fields = {"owners", "chart", "dashboard", "database", "created_by"}
|
allowed_rel_fields = {"owners", "chart", "dashboard", "database", "created_by"}
|
||||||
filter_rel_fields = {
|
filter_rel_fields = {
|
||||||
|
|
|
@ -20,8 +20,10 @@ from croniter import croniter
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from marshmallow import fields, Schema, validate, validates_schema
|
from marshmallow import fields, Schema, validate, validates_schema
|
||||||
from marshmallow.validate import Length, Range, ValidationError
|
from marshmallow.validate import Length, Range, ValidationError
|
||||||
|
from marshmallow_enum import EnumField
|
||||||
|
|
||||||
from superset.models.reports import (
|
from superset.models.reports import (
|
||||||
|
ReportCreationMethodType,
|
||||||
ReportDataFormat,
|
ReportDataFormat,
|
||||||
ReportRecipientType,
|
ReportRecipientType,
|
||||||
ReportScheduleType,
|
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"
|
"If an alert is staled at a working state, how long until it's state is reseted to"
|
||||||
" error"
|
" 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:
|
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"
|
description=sql_description, example="SELECT value FROM time_series_table"
|
||||||
)
|
)
|
||||||
chart = fields.Integer(required=False, allow_none=True)
|
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)
|
dashboard = fields.Integer(required=False, allow_none=True)
|
||||||
database = fields.Integer(required=False)
|
database = fields.Integer(required=False)
|
||||||
owners = fields.List(fields.Integer(description=owners_description))
|
owners = fields.List(fields.Integer(description=owners_description))
|
||||||
|
@ -226,6 +238,12 @@ class ReportSchedulePutSchema(Schema):
|
||||||
allow_none=True,
|
allow_none=True,
|
||||||
)
|
)
|
||||||
chart = fields.Integer(required=False, 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)
|
dashboard = fields.Integer(required=False, allow_none=True)
|
||||||
database = fields.Integer(required=False)
|
database = fields.Integer(required=False)
|
||||||
owners = fields.List(fields.Integer(description=owners_description), required=False)
|
owners = fields.List(fields.Integer(description=owners_description), required=False)
|
||||||
|
|
|
@ -29,6 +29,7 @@ from superset.models.slice import Slice
|
||||||
from superset.models.dashboard import Dashboard
|
from superset.models.dashboard import Dashboard
|
||||||
from superset.models.reports import (
|
from superset.models.reports import (
|
||||||
ReportSchedule,
|
ReportSchedule,
|
||||||
|
ReportCreationMethodType,
|
||||||
ReportRecipients,
|
ReportRecipients,
|
||||||
ReportExecutionLog,
|
ReportExecutionLog,
|
||||||
ReportScheduleType,
|
ReportScheduleType,
|
||||||
|
@ -269,6 +270,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"changed_on_delta_humanized",
|
"changed_on_delta_humanized",
|
||||||
"created_by",
|
"created_by",
|
||||||
"created_on",
|
"created_on",
|
||||||
|
"creation_method",
|
||||||
"crontab",
|
"crontab",
|
||||||
"crontab_humanized",
|
"crontab_humanized",
|
||||||
"id",
|
"id",
|
||||||
|
@ -442,6 +444,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
"type": ReportRecipientType.EMAIL,
|
"type": ReportRecipientType.EMAIL,
|
||||||
|
@ -470,6 +473,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
assert created_model.crontab == report_schedule_data["crontab"]
|
assert created_model.crontab == report_schedule_data["crontab"]
|
||||||
assert created_model.chart.id == report_schedule_data["chart"]
|
assert created_model.chart.id == report_schedule_data["chart"]
|
||||||
assert created_model.database.id == report_schedule_data["database"]
|
assert created_model.database.id == report_schedule_data["database"]
|
||||||
|
assert created_model.creation_method == report_schedule_data["creation_method"]
|
||||||
# Rollback changes
|
# Rollback changes
|
||||||
db.session.delete(created_model)
|
db.session.delete(created_model)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -487,6 +491,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "name3",
|
"name": "name3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"chart": chart.id,
|
"chart": chart.id,
|
||||||
"database": example_db.id,
|
"database": example_db.id,
|
||||||
|
@ -503,6 +508,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"name": "name3",
|
"name": "name3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"chart": chart.id,
|
"chart": chart.id,
|
||||||
}
|
}
|
||||||
uri = "api/v1/report/"
|
uri = "api/v1/report/"
|
||||||
|
@ -532,6 +538,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.REPORT,
|
"type": ReportScheduleType.REPORT,
|
||||||
"name": "name3",
|
"name": "name3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"chart": chart.id,
|
"chart": chart.id,
|
||||||
"database": example_db.id,
|
"database": example_db.id,
|
||||||
|
@ -545,6 +552,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
|
@ -569,6 +577,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
|
@ -592,6 +601,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
|
@ -617,6 +627,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "new4",
|
"name": "new4",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
|
@ -642,6 +653,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "new5",
|
"name": "new5",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"recipients": [
|
"recipients": [
|
||||||
{
|
{
|
||||||
|
@ -678,6 +690,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"chart": chart.id,
|
"chart": chart.id,
|
||||||
"dashboard": dashboard.id,
|
"dashboard": dashboard.id,
|
||||||
"database": example_db.id,
|
"database": example_db.id,
|
||||||
|
@ -701,6 +714,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"chart": chart.id,
|
"chart": chart.id,
|
||||||
}
|
}
|
||||||
|
@ -726,6 +740,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"type": ReportScheduleType.ALERT,
|
"type": ReportScheduleType.ALERT,
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
"chart": chart_max_id + 1,
|
"chart": chart_max_id + 1,
|
||||||
"database": database_max_id + 1,
|
"database": database_max_id + 1,
|
||||||
|
@ -748,6 +763,7 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
"name": "new3",
|
"name": "new3",
|
||||||
"description": "description",
|
"description": "description",
|
||||||
"crontab": "0 9 * * *",
|
"crontab": "0 9 * * *",
|
||||||
|
"creation_method": ReportCreationMethodType.ALERTS_REPORTS,
|
||||||
"dashboard": dashboard_max_id + 1,
|
"dashboard": dashboard_max_id + 1,
|
||||||
"database": examples_db.id,
|
"database": examples_db.id,
|
||||||
}
|
}
|
||||||
|
@ -757,6 +773,82 @@ class TestReportSchedulesApi(SupersetTestCase):
|
||||||
data = json.loads(rv.data.decode("utf-8"))
|
data = json.loads(rv.data.decode("utf-8"))
|
||||||
assert data == {"message": {"dashboard": "Dashboard does not exist"}}
|
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")
|
@pytest.mark.usefixtures("create_report_schedules")
|
||||||
def test_update_report_schedule(self):
|
def test_update_report_schedule(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue