Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dashboard] New, add statsd incr to the API #9519

Merged
merged 12 commits into from
Apr 16, 2020
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__)
dpgaspar marked this conversation as resolved.
Show resolved Hide resolved
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