Skip to content
26 changes: 26 additions & 0 deletions superset/dashboards/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,19 +160,25 @@ def post(self) -> Response:
500:
$ref: '#/components/responses/500'
"""
self.incr_stats("init", self.post.__name__)
Comment thread
dpgaspar marked this conversation as resolved.
Outdated
if not request.is_json:
self.incr_stats("error", self.post.__name__)
return self.response_400(message="Request is not JSON")
item = self.add_model_schema.load(request.json)
# This validates custom Schema with custom validations
if item.errors:
self.incr_stats("error", self.post.__name__)
return self.response_400(message=item.errors)
try:
new_model = CreateDashboardCommand(g.user, item.data).run()
self.incr_stats("success", self.post.__name__)
return self.response(201, id=new_model.id, result=item.data)
except DashboardInvalidError as ex:
self.incr_stats("error", self.post.__name__)
return self.response_422(message=ex.normalized_messages())
except DashboardCreateFailedError as ex:
logger.error(f"Error creating model {self.__class__.__name__}: {ex}")
self.incr_stats("error", self.post.__name__)
return self.response_422(message=str(ex))

@expose("/<pk>", methods=["PUT"])
Expand Down Expand Up @@ -223,23 +229,31 @@ def put( # pylint: disable=too-many-return-statements, arguments-differ
500:
$ref: '#/components/responses/500'
"""
self.incr_stats("init", self.put.__name__)
if not request.is_json:
self.incr_stats("error", self.put.__name__)
return self.response_400(message="Request is not JSON")
item = self.edit_model_schema.load(request.json)
# This validates custom Schema with custom validations
if item.errors:
self.incr_stats("error", self.put.__name__)
return self.response_400(message=item.errors)
try:
changed_model = UpdateDashboardCommand(g.user, pk, item.data).run()
self.incr_stats("success", self.put.__name__)
return self.response(200, id=changed_model.id, result=item.data)
except DashboardNotFoundError:
self.incr_stats("error", self.put.__name__)
return self.response_404()
except DashboardForbiddenError:
self.incr_stats("error", self.put.__name__)
return self.response_403()
except DashboardInvalidError as ex:
self.incr_stats("error", self.put.__name__)
return self.response_422(message=ex.normalized_messages())
except DashboardUpdateFailedError as ex:
logger.error(f"Error updating model {self.__class__.__name__}: {ex}")
self.incr_stats("error", self.put.__name__)
return self.response_422(message=str(ex))

@expose("/<pk>", methods=["DELETE"])
Expand Down Expand Up @@ -277,15 +291,20 @@ def delete(self, pk: int) -> Response: # pylint: disable=arguments-differ
500:
$ref: '#/components/responses/500'
"""
self.incr_stats("init", self.delete.__name__)
try:
DeleteDashboardCommand(g.user, pk).run()
self.incr_stats("success", self.delete.__name__)
return self.response(200, message="OK")
except DashboardNotFoundError:
self.incr_stats("error", self.delete.__name__)
return self.response_404()
except DashboardForbiddenError:
self.incr_stats("error", self.delete.__name__)
return self.response_403()
except DashboardDeleteFailedError as ex:
logger.error(f"Error deleting model {self.__class__.__name__}: {ex}")
self.incr_stats("error", self.delete.__name__)
return self.response_422(message=str(ex))

@expose("/", methods=["DELETE"])
Expand Down Expand Up @@ -330,9 +349,11 @@ def bulk_delete(
500:
$ref: '#/components/responses/500'
"""
self.incr_stats("init", self.bulk_delete.__name__)
item_ids = kwargs["rison"]
try:
BulkDeleteDashboardCommand(g.user, item_ids).run()
self.incr_stats("success", self.bulk_delete.__name__)
return self.response(
200,
message=ngettext(
Expand All @@ -342,10 +363,13 @@ def bulk_delete(
),
)
except DashboardNotFoundError:
self.incr_stats("error", self.bulk_delete.__name__)
return self.response_404()
except DashboardForbiddenError:
self.incr_stats("error", self.bulk_delete.__name__)
return self.response_403()
except DashboardBulkDeleteFailedError as ex:
self.incr_stats("error", self.bulk_delete.__name__)
return self.response_422(message=str(ex))

@expose("/export/", methods=["GET"])
Expand Down Expand Up @@ -385,6 +409,7 @@ def export(self, **kwargs: Any) -> Response:
500:
$ref: '#/components/responses/500'
"""
self.incr_stats("init", self.bulk_delete.__name__)
query = self.datamodel.session.query(Dashboard).filter(
Dashboard.id.in_(kwargs["rison"])
)
Expand All @@ -397,4 +422,5 @@ def export(self, **kwargs: Any) -> Response:
resp.headers["Content-Disposition"] = generate_download_headers("json")[
"Content-Disposition"
]
self.incr_stats("success", self.bulk_delete.__name__)
return resp
33 changes: 32 additions & 1 deletion superset/views/base_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import logging
from typing import cast, Dict, Set, Tuple, Type, Union

from flask import request
from flask import request, Response
from flask_appbuilder import ModelRestApi
from flask_appbuilder.api import expose, protect, rison, safe
from flask_appbuilder.models.filters import BaseFilter, Filters
Expand Down Expand Up @@ -153,6 +153,33 @@ def _get_related_filter(self, datamodel, column_name: str, value: str) -> Filter
def incr_stats(self, action: str, func_name: str) -> None:
self.stats_logger.incr(f"{self.__class__.__name__}.{func_name}.{action}")

def info_headless(self, **kwargs) -> Response:
self.incr_stats("init", self.info.__name__)
response = super().info_headless(**kwargs)
if response.status_code == 200:
self.incr_stats("success", self.get.__name__)
else:
self.incr_stats("error", self.get.__name__)
return response

def get_headless(self, pk, **kwargs) -> Response:
self.incr_stats("init", self.get.__name__)
response = super().get_headless(pk, **kwargs)
if response.status_code == 200:
self.incr_stats("success", self.get.__name__)
else:
self.incr_stats("error", self.get.__name__)
return response

def get_list_headless(self, **kwargs) -> Response:
self.incr_stats("init", self.get_list.__name__)
response = super().get_list_headless(**kwargs)
if response.status_code == 200:
self.incr_stats("success", self.get.__name__)
else:
self.incr_stats("error", self.get.__name__)
return response

@expose("/related/<column_name>", methods=["GET"])
@protect()
@safe
Expand Down Expand Up @@ -207,14 +234,17 @@ def related(self, column_name: str, **kwargs):
500:
$ref: '#/components/responses/500'
"""
self.incr_stats("init", self.related.__name__)
if column_name not in self.allowed_rel_fields:
self.incr_stats("error", self.related.__name__)
return self.response_404()
args = kwargs.get("rison", {})
# handle pagination
page, page_size = self._handle_page_args(args)
try:
datamodel = self.datamodel.get_related_interface(column_name)
except KeyError:
self.incr_stats("error", self.related.__name__)
return self.response_404()
page, page_size = self._sanitize_page_args(page, page_size)
# handle ordering
Expand All @@ -234,6 +264,7 @@ def related(self, column_name: str, **kwargs):
{"value": datamodel.get_pk_value(value), "text": str(value)}
for value in values
]
self.incr_stats("success", self.related.__name__)
return self.response(200, count=count, result=result)


Expand Down