Projet

Général

Profil

0001-misc-remove-the-use-of-to_json-decorator-for-modern-.patch

Lauréline Guérin, 20 mars 2020 11:09

Télécharger (8,28 ko)

Voir les différences:

Subject: [PATCH] misc: remove the use of to_json decorator for modern
 endpoints (#25690)

 passerelle/views.py  | 105 ++++++++++++++++++++++++++++++++++++++++---
 tests/test_mdph13.py |   5 ---
 2 files changed, 100 insertions(+), 10 deletions(-)
passerelle/views.py
18 18
import hashlib
19 19
import inspect
20 20
import json
21
import re
21 22

  
22 23
from django.apps import apps
23 24
from django.conf.urls import url
24 25
from django.core.cache import cache
25
from django.core.exceptions import PermissionDenied
26
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
26 27
from django.contrib.auth import logout as auth_logout
27 28
from django.contrib.auth import views as auth_views
28 29
from django.db import transaction
29 30
from django.db.models import TextField
30 31
from django.db.models.functions import Cast
31
from django.http import HttpResponse, HttpResponseRedirect, Http404
32
from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponseBadRequest
33
from django.http.response import HttpResponseBase
32 34
from django.views.decorators.csrf import csrf_exempt
33 35
from django.views.generic import (
34 36
    RedirectView, View, TemplateView, CreateView, DeleteView, UpdateView,
......
46 48

  
47 49
from dateutil import parser as date_parser
48 50
from jsonschema import validate, ValidationError
51
from requests import RequestException
49 52

  
50 53
from passerelle.base.models import BaseResource, ResourceLog
51 54
from passerelle.compat import json_loads
52
from passerelle.utils.jsonresponse import APIError
55
from passerelle.utils.conversion import exception_to_text
53 56
from passerelle.utils.json import unflatten
57
from passerelle.utils.jsonresponse import APIError
58
from passerelle.utils.jsonresponse import CALLBACK_NAME
59
from passerelle.utils.jsonresponse import DEFAULT_DEBUG
60
from passerelle.utils.jsonresponse import JSONEncoder
54 61

  
55
from .utils import to_json, is_authorized
62
from .utils import is_authorized
56 63
from .forms import GenericConnectorForm
57 64

  
58 65
if 'mellon' in settings.INSTALLED_APPS:
......
75 82
                                        + quote(request.GET.get('next')))
76 83
        return super(LoginView, self).dispatch(request, *args, **kwargs)
77 84

  
85

  
78 86
login = LoginView.as_view()
79 87

  
80 88

  
......
305 313
    def __str__(self):
306 314
        return 'invalid value for parameter "%s"' % self.parameter_name
307 315

  
316

  
308 317
IGNORED_PARAMS = ('apikey', 'signature', 'nonce', 'algo', 'timestamp', 'orig', 'jsonpCallback',
309 318
                  'callback', '_', 'raise', 'debug', 'decode', 'format')
310 319

  
......
469 478
            cache.set(cache_key, result, self.endpoint.endpoint_info.cache_duration)
470 479
        return result
471 480

  
481
    def _obj_to_response(self, req, obj):
482
        if isinstance(obj, dict) and 'err' not in obj:
483
            obj['err'] = 0
484
        return obj
485

  
486
    def _err_to_response(self, err):
487
        if hasattr(err, "__module__"):
488
            err_module = err.__module__ + "."
489
        else:
490
            err_module = ""
491

  
492
        if hasattr(err, "owner"):
493
            err_module += err.owner.__name__ + "."
494

  
495
        err_class = err_module + err.__class__.__name__
496

  
497
        err_desc = force_text(err)
498

  
499
        return {
500
            "err": 1,
501
            "err_class": err_class,
502
            "err_desc": err_desc,
503
            "data": getattr(err, 'data', None),
504
        }
505

  
506
    def _render_data(self, req, data, status=200):
507
        debug = DEFAULT_DEBUG
508
        debug = debug or req.GET.get('debug', 'false').lower() in ('true', 't', '1', 'on')
509
        debug = debug or req.GET.get('decode', '0').lower() in ('true', 't', '1', 'on')
510
        if 'callback' in req.GET or 'jsonpCallback' in req.GET:
511
            output_format = req.GET.get('format', 'jsonp')
512
        else:
513
            output_format = req.GET.get('format', 'json')
514
        jsonp_cb = req.GET.get('callback') or req.GET.get('jsonpCallback') or CALLBACK_NAME
515
        if not re.match(r'^[$a-zA-Z_][a-zA-Z0-9_]*$', jsonp_cb):
516
            return HttpResponseBadRequest('invalid JSONP callback name')
517
        content_type = "application/json"
518

  
519
        kwargs = {'cls': JSONEncoder}
520
        if debug:
521
            kwargs["indent"] = 4
522
            kwargs["ensure_ascii"] = False
523
            kwargs["encoding"] = "utf8"
524

  
525
        plain = json.dumps(data, **kwargs)
526
        if output_format == 'jsonp':
527
            plain = "%s(%s);" % (jsonp_cb, plain)
528
            content_type = "application/javascript"
529

  
530
        return HttpResponse(plain, content_type="%s; charset=UTF-8" % content_type, status=status)
531

  
472 532
    def get(self, request, *args, **kwargs):
473 533
        if self.endpoint.endpoint_info.pattern:
474 534
            pattern = url(self.endpoint.endpoint_info.pattern, self.endpoint)
......
478 538
            kwargs['other_params'] = match.kwargs
479 539
        elif kwargs.get('rest'):
480 540
            raise Http404()
541

  
481 542
        connector = self.get_object()
482
        return to_json(logger=connector.logger)(self.perform)(request, *args, **kwargs)
543

  
544
        try:
545
            resp = self.perform(request, *args, **kwargs)
546
            if isinstance(resp, HttpResponseBase):
547
                return resp
548

  
549
            data = self._obj_to_response(request, resp)
550
            status = 200
551
        except Exception as e:
552
            extras = {'method': request.method, 'exception': exception_to_text(e), 'request': request}
553
            if request.method == 'POST':
554
                max_size = connector.logging_parameters.requests_max_size
555
                extras.update({'body': repr(request.body[:max_size])})
556
            if (not isinstance(e, (Http404, PermissionDenied, ObjectDoesNotExist, RequestException))
557
                    and getattr(e, 'log_error', True)):
558
                connector.logger.exception("Error occurred while processing request", extra=extras)
559
            elif isinstance(e, APIError):
560
                connector.logger.warning("Error occurred while processing request", extra=extras)
561

  
562
            if int(request.GET.get('raise', 0)):
563
                raise
564

  
565
            data = self._err_to_response(e)
566
            if getattr(e, 'err_code', None):
567
                data['err'] = e.err_code
568
            if getattr(e, 'http_status', None):
569
                status = e.http_status
570
            elif isinstance(e, (ObjectDoesNotExist, Http404)):
571
                status = 404
572
            elif isinstance(e, PermissionDenied):
573
                status = 403
574
            else:
575
                status = 500
576

  
577
        return self._render_data(request, data, status)
483 578

  
484 579
    def post(self, request, *args, **kwargs):
485 580
        return self.get(request, *args, **kwargs)
tests/test_mdph13.py
18 18
import json
19 19
import base64
20 20
import datetime
21
import logging
22 21

  
23 22
import requests
24 23
import requests.exceptions
......
498 497
    mock_http.add_response({'status_code': 401, 'content': 'wtf', 'reason': 'Authentication required'})
499 498
    response = app.post(url, status=500)
500 499
    assert response.json['err_class'] == 'requests.exceptions.HTTPError'
501
    assert caplog.records[-1].levelno == logging.ERROR
502 500
    assert caplog.records[-1].getMessage() == 'GET http://cd13.fr/situation/dossier/1234 (=> 401)'
503
    assert hasattr(caplog.records[-1].request, 'META')
504 501

  
505 502
    def raise_ssl_error(url, request):
506 503
        raise requests.exceptions.SSLError(request=request)
507 504
    mock_http.add_response(raise_ssl_error)
508 505
    response = app.post(url, status=500)
509 506
    assert response.json['err_class'] == 'requests.exceptions.SSLError'
510
    assert caplog.records[-1].levelno == logging.ERROR
511 507
    assert caplog.records[-1].getMessage() == 'GET http://cd13.fr/situation/dossier/1234 (=> SSLError())'
512
    assert hasattr(caplog.records[-1].request, 'META')
513
-