utils/soap: allow zeep versions 2 and 3 (#35370)
This commit is contained in:
parent
85eb135516
commit
00011f6f02
|
@ -24,6 +24,10 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import HttpResponse, Http404
|
||||||
|
|
||||||
from zeep.exceptions import Fault as WebFault, TransportError, XMLSyntaxError
|
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.base.models import BaseResource, HTTPResource
|
||||||
from passerelle.utils.api import endpoint
|
from passerelle.utils.api import endpoint
|
||||||
|
@ -98,9 +102,9 @@ class IParapheur(BaseResource, HTTPResource):
|
||||||
def get_verbose_name(cls):
|
def get_verbose_name(cls):
|
||||||
return cls._meta.verbose_name
|
return cls._meta.verbose_name
|
||||||
|
|
||||||
def get_client(self, strict_mode=True):
|
def get_client(self, **kwargs):
|
||||||
try:
|
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.
|
# overrides the service port address URL defined in the WSDL.
|
||||||
if self.wsdl_endpoint_location:
|
if self.wsdl_endpoint_location:
|
||||||
|
@ -116,7 +120,10 @@ class IParapheur(BaseResource, HTTPResource):
|
||||||
|
|
||||||
def call(self, service_name, *args, **kwargs):
|
def call(self, service_name, *args, **kwargs):
|
||||||
strict_mode = kwargs.pop('strict_mode', True)
|
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:
|
try:
|
||||||
result = getattr(client.overridden_service, service_name)(*args, **kwargs)
|
result = getattr(client.overridden_service, service_name)(*args, **kwargs)
|
||||||
except WebFault as exc:
|
except WebFault as exc:
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -104,7 +104,7 @@ setup(name='passerelle',
|
||||||
'python-dateutil',
|
'python-dateutil',
|
||||||
'Pillow',
|
'Pillow',
|
||||||
'jsonschema < 3.1',
|
'jsonschema < 3.1',
|
||||||
'zeep < 3.0',
|
'zeep',
|
||||||
'pycrypto',
|
'pycrypto',
|
||||||
'unidecode',
|
'unidecode',
|
||||||
'paramiko',
|
'paramiko',
|
||||||
|
|
|
@ -48,9 +48,10 @@
|
||||||
</element>
|
</element>
|
||||||
<element name="TradePrice">
|
<element name="TradePrice">
|
||||||
<complexType>
|
<complexType>
|
||||||
<all>
|
<sequence>
|
||||||
|
<element name="skipMe" type="float"/>
|
||||||
<element name="price" type="float"/>
|
<element name="price" type="float"/>
|
||||||
</all>
|
</sequence>
|
||||||
</complexType>
|
</complexType>
|
||||||
</element>
|
</element>
|
||||||
<complexType name="account">
|
<complexType name="account">
|
||||||
|
|
|
@ -8,6 +8,7 @@ import base64
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from requests import Response
|
from requests import Response
|
||||||
|
from zeep import __version__ as zeep_version
|
||||||
|
|
||||||
from requests.exceptions import ConnectionError
|
from requests.exceptions import ConnectionError
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
@ -41,6 +42,12 @@ def conn():
|
||||||
resource_pk=conn.pk)
|
resource_pk=conn.pk)
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
def assert_invalid_xml(err_desc):
|
||||||
|
if zeep_version < '3':
|
||||||
|
assert "Server returned HTTP status 200 (<nada>)" in err_desc
|
||||||
|
else:
|
||||||
|
assert "Server returned response (200) with invalid XML" in err_desc
|
||||||
|
|
||||||
def xmlmime():
|
def xmlmime():
|
||||||
return os.path.join(os.path.dirname(__file__), 'data','xmlmime.xml')
|
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):
|
def test_call_ping(soap_client, app, conn):
|
||||||
service = mock.Mock()
|
service = mock.Mock()
|
||||||
service.echo.return_value = 'pong'
|
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
|
soap_client.return_value = mocked_client
|
||||||
url = reverse('generic-endpoint', kwargs={'connector': 'iparapheur',
|
url = reverse('generic-endpoint', kwargs={'connector': 'iparapheur',
|
||||||
'endpoint': 'ping', 'slug': conn.slug})
|
'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 (BASE_URL,) == mocked_post.call_args[0]
|
||||||
assert resp.json['err'] == 1
|
assert resp.json['err'] == 1
|
||||||
assert 'zeep.exceptions.TransportError' in resp.json['err_class']
|
assert 'zeep.exceptions.TransportError' in resp.json['err_class']
|
||||||
assert 'Server returned HTTP status 200 (<nada>)' in resp.json['err_desc']
|
assert_invalid_xml(resp.json['err_desc'])
|
||||||
|
|
||||||
# Unknown value for "visibility"
|
# Unknown value for "visibility"
|
||||||
err_data = data.copy()
|
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 (BASE_URL,) == mocked_post.call_args[0]
|
||||||
assert resp.json['err'] == 1
|
assert resp.json['err'] == 1
|
||||||
assert 'zeep.exceptions.TransportError' in resp.json['err_class']
|
assert 'zeep.exceptions.TransportError' in resp.json['err_class']
|
||||||
assert 'Server returned HTTP status 200 (<nada>)' 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.get', side_effect=iph_mocked_get)
|
||||||
@mock.patch('passerelle.utils.Request.post')
|
@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 resp.json['err'] == 1
|
||||||
#assert 'zeep.exceptions.TransportError' in resp.json['err_class']
|
#assert 'zeep.exceptions.TransportError' in resp.json['err_class']
|
||||||
assert 'passerelle.utils.jsonresponse.APIError' in resp.json['err_class']
|
assert 'passerelle.utils.jsonresponse.APIError' in resp.json['err_class']
|
||||||
assert 'Server returned HTTP status 200 (<nada>)' 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.get', side_effect=iph_mocked_get)
|
||||||
@mock.patch('passerelle.utils.Request.post')
|
@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'
|
assert resp.text == 'Test Document'
|
||||||
|
|
||||||
# KO
|
# KO
|
||||||
soap_response = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><CreerDossierResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"><MessageRetour><codeRetour>KO</codeRetour><message>KOmessage</message><severite>INFO</severite></MessageRetour></CreerDossierResponse></S:Body></S:Envelope>"""
|
soap_response = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><GetDossierResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"><MessageRetour><codeRetour>KO</codeRetour><message>KOmessage</message><severite>INFO</severite></MessageRetour></GetDossierResponse></S:Body></S:Envelope>"""
|
||||||
response._content = soap_response
|
response._content = soap_response
|
||||||
mocked_post.return_value = response
|
mocked_post.return_value = response
|
||||||
resp = app.get(url, status=500)
|
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'
|
assert resp.json['err_desc'] == 'KOmessage'
|
||||||
|
|
||||||
# unknown response
|
# unknown response
|
||||||
soap_response = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><CreerDossierResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"></CreerDossierResponse></S:Body></S:Envelope>"""
|
soap_response = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><GetDossierResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"></GetDossierResponse></S:Body></S:Envelope>"""
|
||||||
response._content = soap_response
|
response._content = soap_response
|
||||||
mocked_post.return_value = response
|
mocked_post.return_value = response
|
||||||
resp = app.get(url, status=500)
|
resp = app.get(url, status=500)
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
|
import pytest
|
||||||
|
import mock
|
||||||
import requests
|
import requests
|
||||||
from zeep.plugins import Plugin
|
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
|
from passerelle.utils.soap import SOAPClient
|
||||||
|
|
||||||
|
@ -30,3 +37,32 @@ def test_soap_client():
|
||||||
assert client.transport.session == soap_resource.requests
|
assert client.transport.session == soap_resource.requests
|
||||||
assert client.transport.cache
|
assert client.transport.cache
|
||||||
assert client.plugins == plugins
|
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 = '''<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<soap-env:Body>
|
||||||
|
<ns0:TradePrice xmlns:ns0="http://example.com/stockquote.xsd">
|
||||||
|
<price>4.20</price>
|
||||||
|
</ns0:TradePrice>
|
||||||
|
</soap-env:Body>
|
||||||
|
</soap-env:Envelope>'''
|
||||||
|
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
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -1,6 +1,6 @@
|
||||||
[tox]
|
[tox]
|
||||||
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/passerelle/{env:BRANCH_NAME:}
|
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/passerelle/{env:BRANCH_NAME:}
|
||||||
envlist = django111-pg
|
envlist = django111-pg-zeep2
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
|
@ -36,6 +36,8 @@ deps =
|
||||||
vobject
|
vobject
|
||||||
django-ratelimit
|
django-ratelimit
|
||||||
pyquery
|
pyquery
|
||||||
|
zeep2: zeep < 3.0
|
||||||
|
zeep3: zeep >= 3.0
|
||||||
commands =
|
commands =
|
||||||
./get_wcs.sh
|
./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/}
|
django111: py.test {posargs: {env:FAST:} --junitxml=test_{envname}_results.xml --cov-report xml --cov-report html --cov=passerelle/ --cov-config .coveragerc tests/}
|
||||||
|
|
Loading…
Reference in New Issue