From 00011f6f02fd760c3fa96e3b3e2200353994edc8 Mon Sep 17 00:00:00 2001 From: Nicolas ROCHE Date: Tue, 15 Oct 2019 10:39:01 +0200 Subject: [PATCH] utils/soap: allow zeep versions 2 and 3 (#35370) --- passerelle/contrib/iparapheur/models.py | 13 ++++++--- setup.py | 2 +- tests/data/soap.wsdl | 5 ++-- tests/test_iparapheur.py | 28 ++++++++++++++----- tests/test_soap.py | 36 +++++++++++++++++++++++++ tox.ini | 4 ++- 6 files changed, 75 insertions(+), 13 deletions(-) diff --git a/passerelle/contrib/iparapheur/models.py b/passerelle/contrib/iparapheur/models.py index 521ed05c..8e7235e2 100644 --- a/passerelle/contrib/iparapheur/models.py +++ b/passerelle/contrib/iparapheur/models.py @@ -24,6 +24,10 @@ from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponse, Http404 from zeep.exceptions import Fault as WebFault, TransportError, XMLSyntaxError +try: + from zeep import Settings # zeep version >= 3.x +except ImportError: + Settings = None from passerelle.base.models import BaseResource, HTTPResource from passerelle.utils.api import endpoint @@ -98,9 +102,9 @@ class IParapheur(BaseResource, HTTPResource): def get_verbose_name(cls): return cls._meta.verbose_name - def get_client(self, strict_mode=True): + def get_client(self, **kwargs): try: - soap_client = self.soap_client(strict=strict_mode) + soap_client = self.soap_client(**kwargs) # overrides the service port address URL defined in the WSDL. if self.wsdl_endpoint_location: @@ -116,7 +120,10 @@ class IParapheur(BaseResource, HTTPResource): def call(self, service_name, *args, **kwargs): strict_mode = kwargs.pop('strict_mode', True) - client = self.get_client(strict_mode=strict_mode) + if Settings is not None: # zeep version >= 3.x: + client = self.get_client(settings=Settings(strict=strict_mode)) + else: + client = self.get_client(strict=strict_mode) try: result = getattr(client.overridden_service, service_name)(*args, **kwargs) except WebFault as exc: diff --git a/setup.py b/setup.py index 1820b6d2..8b002d7b 100755 --- a/setup.py +++ b/setup.py @@ -104,7 +104,7 @@ setup(name='passerelle', 'python-dateutil', 'Pillow', 'jsonschema < 3.1', - 'zeep < 3.0', + 'zeep', 'pycrypto', 'unidecode', 'paramiko', diff --git a/tests/data/soap.wsdl b/tests/data/soap.wsdl index 57832709..399af5a1 100644 --- a/tests/data/soap.wsdl +++ b/tests/data/soap.wsdl @@ -48,9 +48,10 @@ - + + - + diff --git a/tests/test_iparapheur.py b/tests/test_iparapheur.py index 88b10654..2df3b048 100644 --- a/tests/test_iparapheur.py +++ b/tests/test_iparapheur.py @@ -8,6 +8,7 @@ import base64 import xml.etree.ElementTree as ET from dateutil import parser from requests import Response +from zeep import __version__ as zeep_version from requests.exceptions import ConnectionError from django.core.urlresolvers import reverse @@ -41,6 +42,12 @@ def conn(): resource_pk=conn.pk) return conn +def assert_invalid_xml(err_desc): + if zeep_version < '3': + assert "Server returned HTTP status 200 ()" in err_desc + else: + assert "Server returned response (200) with invalid XML" in err_desc + def xmlmime(): return os.path.join(os.path.dirname(__file__), 'data','xmlmime.xml') @@ -70,7 +77,16 @@ def iph_mocked_get(url, params=None, **kwargs): def test_call_ping(soap_client, app, conn): service = mock.Mock() service.echo.return_value = 'pong' - mocked_client = mock.Mock(overridden_service=service) + + class MockedSettings(object): + def __init__(self, **kwargs): + pass + def __enter__(self): + pass + def __exit__(self, exc_type, exc_value, traceback): + pass + + mocked_client = mock.Mock(overridden_service=service, settings=MockedSettings) soap_client.return_value = mocked_client url = reverse('generic-endpoint', kwargs={'connector': 'iparapheur', 'endpoint': 'ping', 'slug': conn.slug}) @@ -187,7 +203,7 @@ def test_create_file(mocked_post, mocked_get, app, conn): assert (BASE_URL,) == mocked_post.call_args[0] assert resp.json['err'] == 1 assert 'zeep.exceptions.TransportError' in resp.json['err_class'] - assert 'Server returned HTTP status 200 ()' in resp.json['err_desc'] + assert_invalid_xml(resp.json['err_desc']) # Unknown value for "visibility" err_data = data.copy() @@ -211,7 +227,7 @@ def test_create_file(mocked_post, mocked_get, app, conn): assert (BASE_URL,) == mocked_post.call_args[0] assert resp.json['err'] == 1 assert 'zeep.exceptions.TransportError' in resp.json['err_class'] - assert 'Server returned HTTP status 200 ()' in resp.json['err_desc'] + assert_invalid_xml(resp.json['err_desc']) @mock.patch('passerelle.utils.Request.get', side_effect=iph_mocked_get) @mock.patch('passerelle.utils.Request.post') @@ -300,7 +316,7 @@ def test_get_file_status(mocked_post, mocked_get, app, conn): assert resp.json['err'] == 1 #assert 'zeep.exceptions.TransportError' in resp.json['err_class'] assert 'passerelle.utils.jsonresponse.APIError' in resp.json['err_class'] - assert 'Server returned HTTP status 200 ()' in resp.json['err_desc'] + assert_invalid_xml(resp.json['err_desc']) @mock.patch('passerelle.utils.Request.get', side_effect=iph_mocked_get) @mock.patch('passerelle.utils.Request.post') @@ -325,7 +341,7 @@ def test_get_file(mocked_post, mocked_get, app, conn): assert resp.text == 'Test Document' # KO - soap_response = """KOKOmessageINFO""" + soap_response = """KOKOmessageINFO""" response._content = soap_response mocked_post.return_value = response resp = app.get(url, status=500) @@ -334,7 +350,7 @@ def test_get_file(mocked_post, mocked_get, app, conn): assert resp.json['err_desc'] == 'KOmessage' # unknown response - soap_response = """""" + soap_response = """""" response._content = soap_response mocked_post.return_value = response resp = app.get(url, status=500) diff --git a/tests/test_soap.py b/tests/test_soap.py index 7752707f..b8bc7a59 100644 --- a/tests/test_soap.py +++ b/tests/test_soap.py @@ -1,5 +1,12 @@ +import pytest +import mock import requests from zeep.plugins import Plugin +from zeep.exceptions import XMLParseError +try: + from zeep import Settings # zeep version >= 3.x +except ImportError: + Settings = None from passerelle.utils.soap import SOAPClient @@ -30,3 +37,32 @@ def test_soap_client(): assert client.transport.session == soap_resource.requests assert client.transport.cache assert client.plugins == plugins + +@mock.patch('requests.sessions.Session.post') +def test_disable_strict_mode(mocked_post): + response = requests.Response() + response.status_code = 200 + response._content = ''' + + + + 4.20 + + +''' + mocked_post.return_value = response + + soap_resource = SOAPResource() + client = SOAPClient(soap_resource) + with pytest.raises( + XMLParseError, match="Unexpected element u'price', expected u'skipMe'"): + client.service.GetLastTradePrice(tickerSymbol='banana') + + if Settings is not None: # zeep version >= 3.x: + client = SOAPClient(soap_resource, settings=Settings(strict=False)) + else: + client = SOAPClient(soap_resource, strict=False) + result = client.service.GetLastTradePrice(tickerSymbol='banana') + assert len(result) == 2 + assert result['skipMe'] == None + assert result['price'] == 4.2 diff --git a/tox.ini b/tox.ini index 4779c8bb..9a218c2e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/passerelle/{env:BRANCH_NAME:} -envlist = django111-pg +envlist = django111-pg-zeep2 [testenv] usedevelop = True @@ -36,6 +36,8 @@ deps = vobject django-ratelimit pyquery + zeep2: zeep < 3.0 + zeep3: zeep >= 3.0 commands = ./get_wcs.sh django111: py.test {posargs: {env:FAST:} --junitxml=test_{envname}_results.xml --cov-report xml --cov-report html --cov=passerelle/ --cov-config .coveragerc tests/}