0002-iparapheur-add-handling-of-invalid-response-content.patch
passerelle/contrib/iparapheur/models.py | ||
---|---|---|
24 | 24 |
from django.utils.translation import ugettext_lazy as _ |
25 | 25 |
from django.http import HttpResponse, Http404 |
26 | 26 | |
27 |
from suds import WebFault |
|
28 | ||
27 | 29 |
from passerelle.base.models import BaseResource |
28 | 30 |
from passerelle.utils.api import endpoint |
29 | 31 |
from passerelle.utils.jsonresponse import APIError |
... | ... | |
37 | 39 |
except ConnectionError as e: |
38 | 40 |
raise APIError('i-Parapheur error: %s' % e) |
39 | 41 | |
42 | ||
40 | 43 |
def format_type(t): |
41 | 44 |
return {'id': unicode(t), 'text': unicode(t)} |
42 | 45 | |
... | ... | |
52 | 55 |
http_status = 404 |
53 | 56 | |
54 | 57 | |
58 |
class InvalidResponseContent(Exception): |
|
59 |
http_status = 200 |
|
60 |
log_error = False |
|
61 | ||
62 | ||
63 |
class ServiceError(Exception): |
|
64 |
http_status = 200 |
|
65 |
log_error = False |
|
66 | ||
67 | ||
55 | 68 |
class IParapheur(BaseResource): |
56 | 69 |
wsdl_url = models.CharField(max_length=128, blank=False, |
57 | 70 |
verbose_name=_('WSDL URL'), |
... | ... | |
75 | 88 |
def get_verbose_name(cls): |
76 | 89 |
return cls._meta.verbose_name |
77 | 90 | |
91 |
def call(self, service_name, *args, **kwargs): |
|
92 |
client = get_client(self) |
|
93 |
try: |
|
94 |
result = getattr(client.service, service_name)(*args, **kwargs) |
|
95 |
except(WebFault,) as exc: |
|
96 |
# Remote Service Error: <SOAP-ENV:Fault> in response |
|
97 |
raise APIError('ServiceError: %s' % exc) |
|
98 |
except(Exception,) as exc: |
|
99 |
# Exception different from suds.tranport.TransportError |
|
100 |
if not exc.args or not isinstance(exc.args[0], tuple): |
|
101 |
raise exc |
|
102 |
# TransportError Exception |
|
103 |
raise APIError('Transport Error: %s' % exc) |
|
104 |
return result |
|
105 | ||
78 | 106 |
@endpoint(perm='can_access') |
79 | 107 |
def types(self, request): |
80 |
c = get_client(self) |
|
81 |
return {'data': [format_type(t) for t in c.service.GetListeTypes()]} |
|
108 |
return {'data': [format_type(t) for t in self.call('GetListeTypes')]} |
|
82 | 109 | |
83 | 110 |
@endpoint(perm='can_access') |
84 | 111 |
def ping(self, request): |
85 |
c = get_client(self) |
|
86 |
return {'data': c.service.echo('ping')} |
|
112 |
return {'data': self.call('echo', 'ping')} |
|
87 | 113 | |
88 | 114 |
@endpoint(perm='can_access') |
89 | 115 |
def subtypes(self, request, type=None): |
90 |
c = get_client(self) |
|
91 | 116 |
if type: |
92 |
return {'data': [format_type(t) for t in c.service.GetListeSousTypes(type)]}
|
|
93 |
return {'data': [format_type(t) for t in c.service.GetListeSousTypes()]}
|
|
117 |
return {'data': [format_type(t) for t in self.call('GetListeSousTypes', type)]}
|
|
118 |
return {'data': [format_type(t) for t in self.call('GetListeSousTypes')]}
|
|
94 | 119 | |
95 | 120 |
@endpoint(perm='can_access') |
96 | 121 |
def files(self, request, status=None): |
97 |
c = get_client(self) |
|
98 | 122 |
if status: |
99 |
return {'data': [format_file(f) for f in c.service.RechercherDossiers(Status=status)]}
|
|
100 |
return {'data': [format_file(f) for f in c.service.RechercherDossiers()]}
|
|
123 |
return {'data': [format_file(f) for f in self.call('RechercherDossiers', Status=status)]}
|
|
124 |
return {'data': [format_file(f) for f in self.call('RechercherDossiers')]}
|
|
101 | 125 | |
102 | 126 |
@endpoint(perm='can_access', name='create-file', methods=['post']) |
103 | 127 |
def create_file(self, request, email=None): |
... | ... | |
134 | 158 | |
135 | 159 |
@endpoint(perm='can_access', name='get-file', pattern='(?P<file_id>[\w-]+)') |
136 | 160 |
def get_file(self, request, file_id): |
137 |
client = get_client(self) |
|
138 |
resp = client.service.GetDossier(file_id) |
|
161 |
resp = self.call('GetDossier', file_id) |
|
139 | 162 |
if resp.MessageRetour.codeRetour == 'KO': |
140 | 163 |
if 'inconnu' in resp.MessageRetour.message: |
141 | 164 |
raise Http404(resp.MessageRetour.message) |
... | ... | |
146 | 169 | |
147 | 170 |
@endpoint(perm='can_access', name='get-file-status', pattern='(?P<file_id>[\w-]+)') |
148 | 171 |
def get_file_status(self, request, file_id): |
149 |
c = get_client(self) |
|
150 |
resp = c.service.GetHistoDossier(file_id) |
|
172 |
resp = self.call('GetHistoDossier', file_id) |
|
151 | 173 |
if resp.MessageRetour.codeRetour == 'KO': |
152 | 174 |
if 'inconnu' in resp.MessageRetour.message: |
153 | 175 |
raise Http404(resp.MessageRetour.message) |
passerelle/contrib/iparapheur/soap.py | ||
---|---|---|
22 | 22 | |
23 | 23 |
from suds.client import Client |
24 | 24 |
from suds.transport.http import HttpAuthenticated |
25 |
from suds.transport import Reply |
|
25 |
from suds.transport import Reply, TransportError
|
|
26 | 26 |
from suds.plugin import MessagePlugin, DocumentPlugin |
27 | 27 | |
28 |
from suds.sudsobject import asdict
|
|
28 |
from requests.exceptions import RequestException
|
|
29 | 29 | |
30 | 30 | |
31 | 31 |
class Filter(MessagePlugin): |
... | ... | |
72 | 72 |
def send(self, request): |
73 | 73 |
request.message = request.message.replace("contentType", "xm:contentType") |
74 | 74 |
self.addcredentials(request) |
75 |
resp = self.model.requests.post(request.url, data=request.message, |
|
75 |
try: |
|
76 |
resp = self.model.requests.post(request.url, data=request.message, |
|
76 | 77 |
headers=request.headers, **self.get_requests_kwargs()) |
77 |
return Reply(resp.status_code, resp.headers, resp.content) |
|
78 |
except(RequestException,) as e: |
|
79 |
raise TransportError(e.message.message, None) |
|
80 | ||
81 |
if resp.status_code in (202, 204): |
|
82 |
return None |
|
83 |
elif not resp.ok: |
|
84 |
raise TransportError( |
|
85 |
resp.reason, |
|
86 |
resp.status_code, fp=StringIO.StringIO(resp.content)) |
|
87 |
else: |
|
88 |
return Reply(resp.status_code, resp.headers, resp.content) |
|
78 | 89 | |
79 | 90 |
def get_client(instance): |
80 | 91 |
transport = Transport(instance) |
tests/test_iparapheur.py | ||
---|---|---|
207 | 207 |
file_sent = os.path.join(os.path.dirname(__file__), 'data/iparapheur_test.pdf') |
208 | 208 |
assert resp.headers['Content-Type'] == 'application/pdf' |
209 | 209 |
assert hashlib.md5(resp.body[:8192]).hexdigest() == hashlib.md5(file(file_sent).read()[:8192]).hexdigest() |
210 | ||
211 | ||
212 |
@mock.patch('passerelle.utils.LoggedRequest.get') |
|
213 |
@mock.patch('passerelle.utils.LoggedRequest.post') |
|
214 |
@mock.patch('passerelle.contrib.iparapheur.soap.HttpAuthenticated.open') |
|
215 |
def test_invalid_response(http_open, mocked_post, mocked_get, setup, xmlmime, wsdl_file): |
|
216 |
app, conn = setup |
|
217 |
file_id = str(uuid.uuid4()) |
|
218 | ||
219 |
http_open.return_value = file(xmlmime) |
|
220 |
mocked_get.return_value = mock.Mock(content=file(wsdl_file).read(), |
|
221 |
status_code=200) |
|
222 |
mocked_post.return_value = mock.Mock(status_code=502, |
|
223 |
content='<p>Bad Gateway</p>', reason='Bad Gateway', ok=False) |
|
224 |
url = reverse('generic-endpoint', kwargs={'slug': conn.slug, |
|
225 |
'connector': 'iparapheur', 'endpoint': 'get-file-status', 'rest': file_id}) |
|
226 |
url += '?apikey=%s' % API_KEY |
|
227 |
resp = app.get(url) |
|
228 |
assert resp.json['err_desc'] == "Transport Error: (502, u'Bad Gateway')" |
|
229 | ||
230 | ||
231 |
@mock.patch('passerelle.utils.LoggedRequest.get') |
|
232 |
@mock.patch('passerelle.utils.LoggedRequest.post') |
|
233 |
@mock.patch('passerelle.contrib.iparapheur.soap.HttpAuthenticated.open') |
|
234 |
def test_webfault_response(http_open, mocked_post, mocked_get, setup, xmlmime, wsdl_file): |
|
235 |
app, conn = setup |
|
236 |
file_id = str(uuid.uuid4()) |
|
237 |
url = reverse('generic-endpoint', kwargs={'slug': conn.slug, |
|
238 |
'connector': 'iparapheur', 'endpoint': 'get-file-status', 'rest': file_id}) |
|
239 |
url += '?apikey=%s' % API_KEY |
|
240 | ||
241 |
http_open.return_value = file(xmlmime) |
|
242 |
mocked_get.return_value = mock.Mock(content=file(wsdl_file).read(), |
|
243 |
status_code=200) |
|
244 |
webfault_response = """ |
|
245 |
<?xml version='1.0' encoding='UTF-8'?> |
|
246 |
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> |
|
247 |
<SOAP-ENV:Body> |
|
248 |
<SOAP-ENV:Fault> |
|
249 |
<faultcode xsi:type="xsd:string">SOAP-ENV:Client</faultcode> |
|
250 |
<faultstring xsi:type="xsd:string"> |
|
251 |
Test server error |
|
252 |
</faultstring> |
|
253 |
</SOAP-ENV:Fault> |
|
254 |
</SOAP-ENV:Body> |
|
255 |
</SOAP-ENV:Envelope>""" |
|
256 | ||
257 |
mocked_post.return_value = mock.Mock( |
|
258 |
status_code=200, content=webfault_response, |
|
259 |
ok=True) |
|
260 |
resp = app.get(url) |
|
261 |
assert 'ServiceError' in resp.json['err_desc'] |
|
262 |
assert 'Test server error' in resp.json['err_desc'] |
|
210 |
- |