From df34673f7cc1e071a5c3fdb2e559f82e85b4c756 Mon Sep 17 00:00:00 2001 From: Josue Kouka Date: Wed, 7 Sep 2016 10:42:29 +0200 Subject: [PATCH 2/2] iparapheur: add handling of invalid response content --- passerelle/contrib/iparapheur/models.py | 40 ++++++++++++++++--------- passerelle/contrib/iparapheur/soap.py | 19 +++++++++--- tests/test_iparapheur.py | 53 +++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/passerelle/contrib/iparapheur/models.py b/passerelle/contrib/iparapheur/models.py index 7793cd2..71e7be0 100644 --- a/passerelle/contrib/iparapheur/models.py +++ b/passerelle/contrib/iparapheur/models.py @@ -24,6 +24,8 @@ from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponse, Http404 +from suds import WebFault + from passerelle.base.models import BaseResource from passerelle.utils.api import endpoint from passerelle.utils.jsonresponse import APIError @@ -37,6 +39,7 @@ def get_client(model): except ConnectionError as e: raise APIError('i-Parapheur error: %s' % e) + def format_type(t): return {'id': unicode(t), 'text': unicode(t)} @@ -75,29 +78,40 @@ class IParapheur(BaseResource): def get_verbose_name(cls): return cls._meta.verbose_name + def call(self, service_name, *args, **kwargs): + client = get_client(self) + try: + result = getattr(client.service, service_name)(*args, **kwargs) + except(WebFault,) as exc: + # Remote Service Error: in response + raise APIError('ServiceError: %s' % exc) + except(Exception,) as exc: + # Exception different from suds.tranport.TransportError + if not exc.args or not isinstance(exc.args[0], tuple): + raise exc + # TransportError Exception + raise APIError('Transport Error: %s' % exc) + return result + @endpoint(perm='can_access') def types(self, request): - c = get_client(self) - return {'data': [format_type(t) for t in c.service.GetListeTypes()]} + return {'data': [format_type(t) for t in self.call('GetListeTypes')]} @endpoint(perm='can_access') def ping(self, request): - c = get_client(self) - return {'data': c.service.echo('ping')} + return {'data': self.call('echo', 'ping')} @endpoint(perm='can_access') def subtypes(self, request, type=None): - c = get_client(self) if type: - return {'data': [format_type(t) for t in c.service.GetListeSousTypes(type)]} - return {'data': [format_type(t) for t in c.service.GetListeSousTypes()]} + return {'data': [format_type(t) for t in self.call('GetListeSousTypes', type)]} + return {'data': [format_type(t) for t in self.call('GetListeSousTypes')]} @endpoint(perm='can_access') def files(self, request, status=None): - c = get_client(self) if status: - return {'data': [format_file(f) for f in c.service.RechercherDossiers(Status=status)]} - return {'data': [format_file(f) for f in c.service.RechercherDossiers()]} + return {'data': [format_file(f) for f in self.call('RechercherDossiers', Status=status)]} + return {'data': [format_file(f) for f in self.call('RechercherDossiers')]} @endpoint(perm='can_access', name='create-file', methods=['post']) def create_file(self, request, email=None): @@ -134,8 +148,7 @@ class IParapheur(BaseResource): @endpoint(perm='can_access', name='get-file', pattern='(?P[\w-]+)') def get_file(self, request, file_id): - client = get_client(self) - resp = client.service.GetDossier(file_id) + resp = self.call('GetDossier', file_id) if resp.MessageRetour.codeRetour == 'KO': if 'inconnu' in resp.MessageRetour.message: raise Http404(resp.MessageRetour.message) @@ -146,8 +159,7 @@ class IParapheur(BaseResource): @endpoint(perm='can_access', name='get-file-status', pattern='(?P[\w-]+)') def get_file_status(self, request, file_id): - c = get_client(self) - resp = c.service.GetHistoDossier(file_id) + resp = self.call('GetHistoDossier', file_id) if resp.MessageRetour.codeRetour == 'KO': if 'inconnu' in resp.MessageRetour.message: raise Http404(resp.MessageRetour.message) diff --git a/passerelle/contrib/iparapheur/soap.py b/passerelle/contrib/iparapheur/soap.py index a68ddde..f1040da 100644 --- a/passerelle/contrib/iparapheur/soap.py +++ b/passerelle/contrib/iparapheur/soap.py @@ -22,10 +22,10 @@ import StringIO from suds.client import Client from suds.transport.http import HttpAuthenticated -from suds.transport import Reply +from suds.transport import Reply, TransportError from suds.plugin import MessagePlugin, DocumentPlugin -from suds.sudsobject import asdict +from requests.exceptions import RequestException class Filter(MessagePlugin): @@ -72,9 +72,20 @@ class Transport(HttpAuthenticated): def send(self, request): request.message = request.message.replace("contentType", "xm:contentType") self.addcredentials(request) - resp = self.model.requests.post(request.url, data=request.message, + try: + resp = self.model.requests.post(request.url, data=request.message, headers=request.headers, **self.get_requests_kwargs()) - return Reply(resp.status_code, resp.headers, resp.content) + except(RequestException,) as e: + raise TransportError(e.message.message, None) + + if resp.status_code in (202, 204): + return None + elif not resp.ok: + raise TransportError( + resp.reason, + resp.status_code, fp=StringIO.StringIO(resp.content)) + else: + return Reply(resp.status_code, resp.headers, resp.content) def get_client(instance): transport = Transport(instance) diff --git a/tests/test_iparapheur.py b/tests/test_iparapheur.py index 69f5800..e513561 100644 --- a/tests/test_iparapheur.py +++ b/tests/test_iparapheur.py @@ -207,3 +207,56 @@ def test_get_file(http_open, mocked_post, mocked_get, setup, xmlmime, wsdl_file) file_sent = os.path.join(os.path.dirname(__file__), 'data/iparapheur_test.pdf') assert resp.headers['Content-Type'] == 'application/pdf' assert hashlib.md5(resp.body[:8192]).hexdigest() == hashlib.md5(file(file_sent).read()[:8192]).hexdigest() + + +@mock.patch('passerelle.utils.LoggedRequest.get') +@mock.patch('passerelle.utils.LoggedRequest.post') +@mock.patch('passerelle.contrib.iparapheur.soap.HttpAuthenticated.open') +def test_invalid_response(http_open, mocked_post, mocked_get, setup, xmlmime, wsdl_file): + app, conn = setup + file_id = str(uuid.uuid4()) + + http_open.return_value = file(xmlmime) + mocked_get.return_value = mock.Mock(content=file(wsdl_file).read(), + status_code=200) + mocked_post.return_value = mock.Mock(status_code=502, + content='

Bad Gateway

', reason='Bad Gateway', ok=False) + url = reverse('generic-endpoint', kwargs={'slug': conn.slug, + 'connector': 'iparapheur', 'endpoint': 'get-file-status', 'rest': file_id}) + url += '?apikey=%s' % API_KEY + resp = app.get(url) + assert resp.json['err_desc'] == "Transport Error: (502, u'Bad Gateway')" + + +@mock.patch('passerelle.utils.LoggedRequest.get') +@mock.patch('passerelle.utils.LoggedRequest.post') +@mock.patch('passerelle.contrib.iparapheur.soap.HttpAuthenticated.open') +def test_webfault_response(http_open, mocked_post, mocked_get, setup, xmlmime, wsdl_file): + app, conn = setup + file_id = str(uuid.uuid4()) + url = reverse('generic-endpoint', kwargs={'slug': conn.slug, + 'connector': 'iparapheur', 'endpoint': 'get-file-status', 'rest': file_id}) + url += '?apikey=%s' % API_KEY + + http_open.return_value = file(xmlmime) + mocked_get.return_value = mock.Mock(content=file(wsdl_file).read(), + status_code=200) + webfault_response = """ + + + + + SOAP-ENV:Client + + Test server error + + + + """ + + mocked_post.return_value = mock.Mock( + status_code=200, content=webfault_response, + ok=True) + resp = app.get(url) + assert 'ServiceError' in resp.json['err_desc'] + assert 'Test server error' in resp.json['err_desc'] -- 2.11.0