diff --git a/ChangeLog b/ChangeLog index 3e38fb2..0f0d377 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ those attributes manually if model has such setters. * 0.9.0 add Django 1.8 support. Tests are passed, there are some deprecation warnings. + * 0.9.1 add signals for request and response. See examples in testapp. 2015-04-20 Kirill Pavlov diff --git a/jsonapi/__init__.py b/jsonapi/__init__.py index c184414..8eb8d31 100644 --- a/jsonapi/__init__.py +++ b/jsonapi/__init__.py @@ -1,5 +1,5 @@ """ JSON:API realization.""" -__version = (0, 9, 0) +__version = (0, 9, 1) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ diff --git a/jsonapi/api.py b/jsonapi/api.py index d14884f..891aa82 100644 --- a/jsonapi/api.py +++ b/jsonapi/api.py @@ -22,12 +22,15 @@ class Meta: ) """ +import json +import logging +import time from django.http import HttpResponse, HttpResponseNotAllowed from django.shortcuts import render -import logging -import json + from .exceptions import JSONAPIError from .serializers import DatetimeDecimalEncoder +from .signals import signal_request, signal_response logger = logging.getLogger(__name__) @@ -261,19 +264,28 @@ def handler_view(self, request, resource_name, ids=None): :return django.http.HttpResponse """ - logger.info("{} {}".format(request.method, request.path)) + signal_request.send(sender=self, request=request) + time_start = time.time() self.update_urls(request, resource_name=resource_name, ids=ids) resource = self.resource_map[resource_name] allowed_http_methods = resource.Meta.allowed_methods if request.method not in allowed_http_methods: - return HttpResponseNotAllowed( + response = HttpResponseNotAllowed( permitted_methods=allowed_http_methods) + signal_response.send( + sender=self, request=request, response=response, + duration=time.time() - time_start) + return response if resource.Meta.authenticators: user = resource.authenticate(request) if user is None or not user.is_authenticated(): - return HttpResponse("Not Authenticated", status=401) + response = HttpResponse("Not Authenticated", status=401) + signal_response.send( + sender=self, request=request, response=response, + duration=time.time() - time_start) + return response kwargs = dict(request=request) if ids is not None: @@ -281,14 +293,18 @@ def handler_view(self, request, resource_name, ids=None): try: if request.method == "GET": - return self.handler_view_get(resource, **kwargs) + response = self.handler_view_get(resource, **kwargs) elif request.method == "POST": - return self.handler_view_post(resource, **kwargs) + response = self.handler_view_post(resource, **kwargs) elif request.method == "PUT": - return self.handler_view_put(resource, **kwargs) + response = self.handler_view_put(resource, **kwargs) elif request.method == "DELETE": - return self.handler_view_delete(resource, **kwargs) + response = self.handler_view_delete(resource, **kwargs) except JSONAPIError as e: - return HttpResponse( + response = HttpResponse( json.dumps({"errors": [e.data]}, cls=DatetimeDecimalEncoder), content_type=self.CONTENT_TYPE, status=e.status) + + signal_response.send(sender=self, request=request, response=response, + duration=time.time() - time_start) + return response diff --git a/jsonapi/signals.py b/jsonapi/signals.py new file mode 100644 index 0000000..b413248 --- /dev/null +++ b/jsonapi/signals.py @@ -0,0 +1,5 @@ +""" JSON-API signals.""" +import django.dispatch + +signal_request = django.dispatch.Signal() +signal_response = django.dispatch.Signal() diff --git a/tests/testapp/__init__.py b/tests/testapp/__init__.py index e69de29..4047535 100644 --- a/tests/testapp/__init__.py +++ b/tests/testapp/__init__.py @@ -0,0 +1 @@ +from .signals import log_jsonapi_request diff --git a/tests/testapp/settings/base.py b/tests/testapp/settings/base.py index ea327cb..85003b4 100644 --- a/tests/testapp/settings/base.py +++ b/tests/testapp/settings/base.py @@ -23,7 +23,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', + # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', diff --git a/tests/testapp/settings/dev.py b/tests/testapp/settings/dev.py index f653867..103cd86 100644 --- a/tests/testapp/settings/dev.py +++ b/tests/testapp/settings/dev.py @@ -20,3 +20,27 @@ '127.0.0.1', '33.33.33.1', ] + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'simple': { + 'format': '%(asctime)s [%(levelname)s] \t%(message)s' + }, + }, + 'handlers': { + 'console': { + # 'level': 'DEBUG', + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'simple' + }, + }, + 'loggers': { + '': { + 'handlers': ['console'], + 'level': 'INFO', + }, + } +} diff --git a/tests/testapp/signals.py b/tests/testapp/signals.py new file mode 100644 index 0000000..b6bc5de --- /dev/null +++ b/tests/testapp/signals.py @@ -0,0 +1,34 @@ +from django.dispatch import receiver +from jsonapi.signals import ( + signal_request as jsonapi_signal_request, + signal_response as jsonapi_signal_response, +) + +import logging + +logger = logging.getLogger(__name__) + + +@receiver(jsonapi_signal_request) +def log_jsonapi_request(sender, signal, request=None): + msg = "{} {}".format(request.method, request.get_full_path()) + msg = "{method} {path}".format( + method=request.method, path=request.get_full_path()) + logger.info(msg) + + +@receiver(jsonapi_signal_response) +def log_jsonapi_response(sender, signal, request=None, + response=None, duration=None): + msg = "{method} {path}".format( + method=request.method, + path=request.get_full_path() + ) + if request.body: + msg += " --data '{}'".format(request.body.decode('utf8')) + + msg += "; status={status_code} ({duration:.3f} sec)".format( + duration=duration, + status_code=response.status_code + ) + logger.info(msg)