174 lines
5.1 KiB
Python
174 lines
5.1 KiB
Python
"""" Common utilities for comment client wrapper """
|
|
import logging
|
|
from contextlib import contextmanager
|
|
from time import time
|
|
from uuid import uuid4
|
|
|
|
import requests
|
|
from django.utils.translation import get_language
|
|
|
|
import dogstats_wrapper as dog_stats_api
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def strip_none(dic):
|
|
return dict([(k, v) for k, v in dic.iteritems() if v is not None])
|
|
|
|
|
|
def strip_blank(dic):
|
|
def _is_blank(v):
|
|
return isinstance(v, str) and len(v.strip()) == 0
|
|
return dict([(k, v) for k, v in dic.iteritems() if not _is_blank(v)])
|
|
|
|
|
|
def extract(dic, keys):
|
|
if isinstance(keys, str):
|
|
return strip_none({keys: dic.get(keys)})
|
|
else:
|
|
return strip_none({k: dic.get(k) for k in keys})
|
|
|
|
|
|
def merge_dict(dic1, dic2):
|
|
return dict(dic1.items() + dic2.items())
|
|
|
|
|
|
@contextmanager
|
|
def request_timer(request_id, method, url, tags=None):
|
|
start = time()
|
|
with dog_stats_api.timer('comment_client.request.time', tags=tags):
|
|
yield
|
|
end = time()
|
|
duration = end - start
|
|
|
|
log.info(
|
|
u"comment_client_request_log: request_id={request_id}, method={method}, "
|
|
u"url={url}, duration={duration}".format(
|
|
request_id=request_id,
|
|
method=method,
|
|
url=url,
|
|
duration=duration
|
|
)
|
|
)
|
|
|
|
|
|
def perform_request(method, url, data_or_params=None, raw=False,
|
|
metric_action=None, metric_tags=None, paged_results=False):
|
|
# To avoid dependency conflict
|
|
from django_comment_common.models import ForumsConfig
|
|
config = ForumsConfig.current()
|
|
|
|
if not config.enabled:
|
|
raise CommentClientMaintenanceError('service disabled')
|
|
|
|
if metric_tags is None:
|
|
metric_tags = []
|
|
|
|
metric_tags.append(u'method:{}'.format(method))
|
|
if metric_action:
|
|
metric_tags.append(u'action:{}'.format(metric_action))
|
|
|
|
if data_or_params is None:
|
|
data_or_params = {}
|
|
headers = {
|
|
'X-Edx-Api-Key': config.api_key,
|
|
'Accept-Language': get_language(),
|
|
}
|
|
request_id = uuid4()
|
|
request_id_dict = {'request_id': request_id}
|
|
|
|
if method in ['post', 'put', 'patch']:
|
|
data = data_or_params
|
|
params = request_id_dict
|
|
else:
|
|
data = None
|
|
params = merge_dict(data_or_params, request_id_dict)
|
|
with request_timer(request_id, method, url, metric_tags):
|
|
response = requests.request(
|
|
method,
|
|
url,
|
|
data=data,
|
|
params=params,
|
|
headers=headers,
|
|
timeout=config.connection_timeout
|
|
)
|
|
|
|
metric_tags.append(u'status_code:{}'.format(response.status_code))
|
|
if response.status_code > 200:
|
|
metric_tags.append(u'result:failure')
|
|
else:
|
|
metric_tags.append(u'result:success')
|
|
|
|
dog_stats_api.increment('comment_client.request.count', tags=metric_tags)
|
|
|
|
if 200 < response.status_code < 500:
|
|
raise CommentClientRequestError(response.text, response.status_code)
|
|
# Heroku returns a 503 when an application is in maintenance mode
|
|
elif response.status_code == 503:
|
|
raise CommentClientMaintenanceError(response.text)
|
|
elif response.status_code == 500:
|
|
raise CommentClient500Error(response.text)
|
|
else:
|
|
if raw:
|
|
return response.text
|
|
else:
|
|
try:
|
|
data = response.json()
|
|
except ValueError:
|
|
raise CommentClientError(
|
|
u"Invalid JSON response for request {request_id}; first 100 characters: '{content}'".format(
|
|
request_id=request_id,
|
|
content=response.text[:100]
|
|
)
|
|
)
|
|
if paged_results:
|
|
dog_stats_api.histogram(
|
|
'comment_client.request.paged.result_count',
|
|
value=len(data.get('collection', [])),
|
|
tags=metric_tags
|
|
)
|
|
dog_stats_api.histogram(
|
|
'comment_client.request.paged.page',
|
|
value=data.get('page', 1),
|
|
tags=metric_tags
|
|
)
|
|
dog_stats_api.histogram(
|
|
'comment_client.request.paged.num_pages',
|
|
value=data.get('num_pages', 1),
|
|
tags=metric_tags
|
|
)
|
|
return data
|
|
|
|
|
|
class CommentClientError(Exception):
|
|
def __init__(self, msg):
|
|
self.message = msg
|
|
|
|
def __str__(self):
|
|
return repr(self.message)
|
|
|
|
|
|
class CommentClientRequestError(CommentClientError):
|
|
def __init__(self, msg, status_codes=400):
|
|
super(CommentClientRequestError, self).__init__(msg)
|
|
self.status_code = status_codes
|
|
|
|
|
|
class CommentClient500Error(CommentClientError):
|
|
pass
|
|
|
|
|
|
class CommentClientMaintenanceError(CommentClientError):
|
|
pass
|
|
|
|
|
|
class CommentClientPaginatedResult(object):
|
|
""" class for paginated results returned from comment services"""
|
|
|
|
def __init__(self, collection, page, num_pages, thread_count=0, corrected_text=None):
|
|
self.collection = collection
|
|
self.page = page
|
|
self.num_pages = num_pages
|
|
self.thread_count = thread_count
|
|
self.corrected_text = corrected_text
|