feat: Certify Charts and Dashboards (#17335)

* Certify charts

* Format

* Certify dashboards

* Format

* Refactor card certification

* Clear details when certified by empty

* Show certification in detail page

* Add RTL tests

* Test charts api

* Enhance integration tests

* Lint

* Fix dashboards count

* Format

* Handle empty value

* Handle empty slice

* Downgrade migration

* Indent

* Use alter

* Fix revision

* Fix revision
This commit is contained in:
Geido
2021-11-24 13:42:52 +02:00
committed by GitHub
parent 9576478a5d
commit 83e49fc9ee
28 changed files with 589 additions and 60 deletions

View File

@@ -80,7 +80,7 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
charts = []
admin = self.get_user("admin")
for cx in range(CHARTS_FIXTURE_COUNT - 1):
charts.append(self.insert_chart(f"name{cx}", [admin.id], 1))
charts.append(self.insert_chart(f"name{cx}", [admin.id], 1,))
fav_charts = []
for cx in range(round(CHARTS_FIXTURE_COUNT / 2)):
fav_star = FavStar(
@@ -98,6 +98,29 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
db.session.delete(fav_chart)
db.session.commit()
@pytest.fixture()
def create_certified_charts(self):
with self.create_app().app_context():
certified_charts = []
admin = self.get_user("admin")
for cx in range(CHARTS_FIXTURE_COUNT):
certified_charts.append(
self.insert_chart(
f"certified{cx}",
[admin.id],
1,
certified_by="John Doe",
certification_details="Sample certification",
)
)
yield certified_charts
# rollback changes
for chart in certified_charts:
db.session.delete(chart)
db.session.commit()
@pytest.fixture()
def create_chart_with_report(self):
with self.create_app().app_context():
@@ -420,6 +443,8 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
"datasource_id": 1,
"datasource_type": "table",
"dashboards": dashboards_ids,
"certified_by": "John Doe",
"certification_details": "Sample certification",
}
self.login(username="admin")
uri = f"api/v1/chart/"
@@ -535,6 +560,8 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
"datasource_id": birth_names_table_id,
"datasource_type": "table",
"dashboards": [dash_id],
"certified_by": "Mario Rossi",
"certification_details": "Edited certification",
}
self.login(username="admin")
uri = f"api/v1/chart/{chart_id}"
@@ -553,6 +580,8 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
self.assertEqual(model.datasource_id, birth_names_table_id)
self.assertEqual(model.datasource_type, "table")
self.assertEqual(model.datasource_name, full_table_name)
self.assertEqual(model.certified_by, "Mario Rossi")
self.assertEqual(model.certification_details, "Edited certification")
self.assertIn(model.id, [slice.id for slice in related_dashboard.slices])
db.session.delete(model)
db.session.commit()
@@ -703,6 +732,8 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
self.assertEqual(rv.status_code, 200)
expected_result = {
"cache_timeout": None,
"certified_by": None,
"certification_details": None,
"dashboards": [],
"description": None,
"owners": [
@@ -897,6 +928,36 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], 8)
@pytest.mark.usefixtures("create_certified_charts")
def test_gets_certified_charts_filter(self):
arguments = {
"filters": [{"col": "id", "opr": "chart_is_certified", "value": True,}],
"keys": ["none"],
"columns": ["slice_name"],
}
self.login(username="admin")
uri = f"api/v1/chart/?q={prison.dumps(arguments)}"
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], CHARTS_FIXTURE_COUNT)
@pytest.mark.usefixtures("create_charts")
def test_gets_not_certified_charts_filter(self):
arguments = {
"filters": [{"col": "id", "opr": "chart_is_certified", "value": False,}],
"keys": ["none"],
"columns": ["slice_name"],
}
self.login(username="admin")
uri = f"api/v1/chart/?q={prison.dumps(arguments)}"
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], 17)
@pytest.mark.usefixtures("load_energy_charts")
def test_user_gets_none_filtered_energy_slices(self):
# test filtering on datasource_name

View File

@@ -86,6 +86,8 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
css: str = "",
json_metadata: str = "",
published: bool = False,
certified_by: Optional[str] = None,
certification_details: Optional[str] = None,
) -> Dashboard:
obj_owners = list()
obj_roles = list()
@@ -107,6 +109,8 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
slices=slices,
published=published,
created_by=created_by,
certified_by=certified_by,
certification_details=certification_details,
)
db.session.add(dashboard)
db.session.commit()
@@ -125,6 +129,8 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
f"slug{cx}",
[admin.id],
slices=charts if cx < half_dash_count else [],
certified_by="John Doe",
certification_details="Sample certification",
)
if cx < half_dash_count:
chart = self.insert_chart(f"slice{cx}", [admin.id], 1, params="{}")
@@ -315,6 +321,8 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
rv = self.get_assert_metric(uri, "get")
self.assertEqual(rv.status_code, 200)
expected_result = {
"certified_by": None,
"certification_details": None,
"changed_by": None,
"changed_by_name": "",
"changed_by_url": "",
@@ -611,6 +619,38 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
expected_model.dashboard_title == data["result"][i]["dashboard_title"]
)
@pytest.mark.usefixtures("create_dashboards")
def test_gets_certified_dashboards_filter(self):
arguments = {
"filters": [{"col": "id", "opr": "dashboard_is_certified", "value": True,}],
"keys": ["none"],
"columns": ["dashboard_title"],
}
self.login(username="admin")
uri = f"api/v1/dashboard/?q={prison.dumps(arguments)}"
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], DASHBOARDS_FIXTURE_COUNT)
@pytest.mark.usefixtures("create_dashboards")
def test_gets_not_certified_dashboards_filter(self):
arguments = {
"filters": [
{"col": "id", "opr": "dashboard_is_certified", "value": False,}
],
"keys": ["none"],
"columns": ["dashboard_title"],
}
self.login(username="admin")
uri = f"api/v1/dashboard/?q={prison.dumps(arguments)}"
rv = self.get_assert_metric(uri, "get_list")
self.assertEqual(rv.status_code, 200)
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["count"], 6)
def create_dashboard_import(self):
buf = BytesIO()
with ZipFile(buf, "w") as bundle:

View File

@@ -36,6 +36,8 @@ class InsertChartMixin:
viz_type: Optional[str] = None,
params: Optional[str] = None,
cache_timeout: Optional[int] = None,
certified_by: Optional[str] = None,
certification_details: Optional[str] = None,
) -> Slice:
obj_owners = list()
for owner in owners:
@@ -46,6 +48,8 @@ class InsertChartMixin:
)
slice = Slice(
cache_timeout=cache_timeout,
certified_by=certified_by,
certification_details=certification_details,
created_by=created_by,
datasource_id=datasource.id,
datasource_name=datasource.name,