mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
feat(dashboard): dashboard/id/datasets endpoint (#13523)
* feat(dashboard) dashboard/id/datasets endpoint * schema for dashboard datasets * list instead of map * finish dashboard dataset schema * description * better test * add the dataset schema to the schema list * lint
This commit is contained in:
committed by
GitHub
parent
7656b2e68b
commit
1b95ed7267
@@ -237,6 +237,7 @@ class BaseDatasource(
|
||||
return {
|
||||
# simple fields
|
||||
"id": self.id,
|
||||
"uid": self.uid,
|
||||
"column_formats": self.column_formats,
|
||||
"description": self.description,
|
||||
"database": self.database.data, # pylint: disable=no-member
|
||||
|
||||
@@ -115,4 +115,5 @@ MODEL_API_RW_METHOD_PERMISSION_MAP = {
|
||||
"data": "read",
|
||||
"data_from_cache": "read",
|
||||
"get_charts": "read",
|
||||
"get_datasets": "read",
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ from superset.dashboards.filters import (
|
||||
FilterRelatedRoles,
|
||||
)
|
||||
from superset.dashboards.schemas import (
|
||||
DashboardDatasetSchema,
|
||||
DashboardGetResponseSchema,
|
||||
DashboardPostSchema,
|
||||
DashboardPutSchema,
|
||||
@@ -93,6 +94,7 @@ class DashboardRestApi(BaseSupersetModelRestApi):
|
||||
"bulk_delete", # not using RouteMethod since locally defined
|
||||
"favorite_status",
|
||||
"get_charts",
|
||||
"get_datasets",
|
||||
}
|
||||
resource_name = "dashboard"
|
||||
allow_browser_login = True
|
||||
@@ -169,6 +171,7 @@ class DashboardRestApi(BaseSupersetModelRestApi):
|
||||
edit_model_schema = DashboardPutSchema()
|
||||
chart_entity_response_schema = ChartEntityResponseSchema()
|
||||
dashboard_get_response_schema = DashboardGetResponseSchema()
|
||||
dashboard_dataset_schema = DashboardDatasetSchema()
|
||||
|
||||
base_filters = [["slice", DashboardFilter, lambda: []]]
|
||||
|
||||
@@ -189,6 +192,7 @@ class DashboardRestApi(BaseSupersetModelRestApi):
|
||||
openapi_spec_component_schemas = (
|
||||
ChartEntityResponseSchema,
|
||||
DashboardGetResponseSchema,
|
||||
DashboardDatasetSchema,
|
||||
GetFavStarIdsSchema,
|
||||
)
|
||||
apispec_parameter_schemas = {
|
||||
@@ -252,6 +256,57 @@ class DashboardRestApi(BaseSupersetModelRestApi):
|
||||
except DashboardNotFoundError:
|
||||
return self.response_404()
|
||||
|
||||
@expose("/<id_or_slug>/datasets", methods=["GET"])
|
||||
@protect()
|
||||
@safe
|
||||
@statsd_metrics
|
||||
@event_logger.log_this_with_context(
|
||||
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}.get_datasets",
|
||||
log_to_statsd=False,
|
||||
)
|
||||
def get_datasets(self, id_or_slug: str) -> Response:
|
||||
"""Gets a dashboard's datasets
|
||||
---
|
||||
get:
|
||||
description: >-
|
||||
Returns a list of a dashboard's datasets. Each dataset includes only
|
||||
the information necessary to render the dashboard's charts.
|
||||
parameters:
|
||||
- in: path
|
||||
schema:
|
||||
type: string
|
||||
name: id_or_slug
|
||||
description: Either the id of the dashboard, or its slug
|
||||
responses:
|
||||
200:
|
||||
description: Dashboard dataset definitions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
result:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/DashboardDatasetSchema'
|
||||
302:
|
||||
description: Redirects to the current digest
|
||||
400:
|
||||
$ref: '#/components/responses/400'
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
404:
|
||||
$ref: '#/components/responses/404'
|
||||
"""
|
||||
try:
|
||||
datasets = DashboardDAO.get_datasets_for_dashboard(id_or_slug)
|
||||
result = [
|
||||
self.dashboard_dataset_schema.dump(dataset) for dataset in datasets
|
||||
]
|
||||
return self.response(200, result=result)
|
||||
except DashboardNotFoundError:
|
||||
return self.response_404()
|
||||
|
||||
@expose("/<pk>/charts", methods=["GET"])
|
||||
@protect()
|
||||
@safe
|
||||
|
||||
@@ -29,6 +29,7 @@ from superset.extensions import db
|
||||
from superset.models.core import FavStar, FavStarClassName
|
||||
from superset.models.dashboard import Dashboard, id_or_slug_filter
|
||||
from superset.models.slice import Slice
|
||||
from superset.utils import core
|
||||
from superset.utils.dashboard_filter_scopes_converter import copy_filter_scopes
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -57,6 +58,29 @@ class DashboardDAO(BaseDAO):
|
||||
raise DashboardNotFoundError()
|
||||
return dashboard
|
||||
|
||||
@staticmethod
|
||||
def get_datasets_for_dashboard(id_or_slug: str) -> List[Any]:
|
||||
query = (
|
||||
db.session.query(Dashboard)
|
||||
.filter(id_or_slug_filter(id_or_slug))
|
||||
.outerjoin(Slice, Dashboard.slices)
|
||||
.outerjoin(Slice.table)
|
||||
)
|
||||
# Apply dashboard base filters
|
||||
query = DashboardFilter("id", SQLAInterface(Dashboard, db.session)).apply(
|
||||
query, None
|
||||
)
|
||||
dashboard = query.one_or_none()
|
||||
if not dashboard:
|
||||
raise DashboardNotFoundError()
|
||||
datasource_slices = core.indexed(dashboard.slices, "datasource")
|
||||
data = [
|
||||
datasource.data_for_slices(slices)
|
||||
for datasource, slices in datasource_slices.items()
|
||||
if datasource
|
||||
]
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def get_charts_for_dashboard(dashboard_id: int) -> List[Slice]:
|
||||
query = (
|
||||
|
||||
@@ -158,6 +158,51 @@ class DashboardGetResponseSchema(Schema):
|
||||
table_names = fields.String() # legacy nonsense
|
||||
|
||||
|
||||
class DatabaseSchema(Schema):
|
||||
id = fields.Int()
|
||||
name = fields.String()
|
||||
backend = fields.String()
|
||||
allow_multi_schema_metadata_fetch = fields.Bool() # pylint: disable=invalid-name
|
||||
allows_subquery = fields.Bool()
|
||||
allows_cost_estimate = fields.Bool()
|
||||
allows_virtual_table_explore = fields.Bool()
|
||||
explore_database_id = fields.Int()
|
||||
|
||||
|
||||
class DashboardDatasetSchema(Schema):
|
||||
id = fields.Int()
|
||||
uid = fields.Str()
|
||||
column_formats = fields.Dict()
|
||||
database = fields.Nested(DatabaseSchema)
|
||||
default_endpoint = fields.String()
|
||||
filter_select = fields.Bool()
|
||||
filter_select_enabled = fields.Bool()
|
||||
is_sqllab_view = fields.Bool()
|
||||
name = fields.Str()
|
||||
datasource_name = fields.Str()
|
||||
table_name = fields.Str()
|
||||
type = fields.Str()
|
||||
schema = fields.Str()
|
||||
offset = fields.Int()
|
||||
cache_timeout = fields.Int()
|
||||
params = fields.Str()
|
||||
perm = fields.Str()
|
||||
edit_url = fields.Str()
|
||||
sql = fields.Str()
|
||||
select_star = fields.Str()
|
||||
main_dttm_col = fields.Str()
|
||||
health_check_message = fields.Str()
|
||||
fetch_values_predicate = fields.Str()
|
||||
template_params = fields.Str()
|
||||
owners = fields.List(fields.Int())
|
||||
columns = fields.List(fields.Dict())
|
||||
metrics = fields.List(fields.Dict())
|
||||
order_by_choices = fields.List(fields.List(fields.Str()))
|
||||
verbose_map = fields.Dict(fields.Str(), fields.Str())
|
||||
time_grain_sqla = fields.List(fields.List(fields.Str()))
|
||||
granularity_sqla = fields.List(fields.List(fields.Str()))
|
||||
|
||||
|
||||
class BaseDashboardSchema(Schema):
|
||||
# pylint: disable=no-self-use,unused-argument
|
||||
@post_load
|
||||
|
||||
@@ -170,6 +170,32 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
|
||||
db.session.delete(dashboard)
|
||||
db.session.commit()
|
||||
|
||||
@pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
|
||||
def test_get_dashboard_datasets(self):
|
||||
self.login(username="admin")
|
||||
uri = "api/v1/dashboard/world_health/datasets"
|
||||
response = self.get_assert_metric(uri, "get_datasets")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = json.loads(response.data.decode("utf-8"))
|
||||
dashboard = Dashboard.get("world_health")
|
||||
expected_dataset_ids = set([s.datasource_id for s in dashboard.slices])
|
||||
actual_dataset_ids = set([dataset["id"] for dataset in data["result"]])
|
||||
self.assertEqual(actual_dataset_ids, expected_dataset_ids)
|
||||
|
||||
@pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
|
||||
def test_get_dashboard_datasets_not_found(self):
|
||||
self.login(username="alpha")
|
||||
uri = "api/v1/dashboard/not_found/datasets"
|
||||
response = self.get_assert_metric(uri, "get_datasets")
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
|
||||
def test_get_dashboard_datasets_not_allowed(self):
|
||||
self.login(username="gamma")
|
||||
uri = "api/v1/dashboard/world_health/datasets"
|
||||
response = self.get_assert_metric(uri, "get_datasets")
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@pytest.mark.usefixtures("create_dashboards")
|
||||
def get_dashboard_by_slug(self):
|
||||
self.login(username="admin")
|
||||
|
||||
Reference in New Issue
Block a user