misc: replace interception of zeep.exceptions.Fault (#75649)

* replaced by no interception at all in some places, as SOAPError inherit
  from APIError
* replaced by SOAPFault in cartads_cs as there was a custom handling of
  the soap Faults.
* new SOAPValidationError error is added to handle translation to
  APIError with 400 status and not logging as an error of the connector.
This commit is contained in:
Benjamin Dauvergne 2023-03-21 12:15:42 +01:00 committed by Gitea
parent 1f93f5506d
commit 2de6325c6d
8 changed files with 27 additions and 64 deletions

View File

@ -21,10 +21,8 @@ import urllib
import lxml.etree
from django.db import models
from django.utils import dateformat, dateparse
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from zeep import helpers
from zeep.exceptions import Fault
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
@ -58,10 +56,7 @@ class ATALConnector(BaseResource):
def _soap_call(self, wsdl, method, **kwargs):
client = self._soap_client(wsdl=wsdl)
try:
return getattr(client.service, method)(**kwargs)
except Fault as e:
raise APIError(force_str(e))
return getattr(client.service, method)(**kwargs)
def _basic_ref(self, wsdl, method):
soap_res = self._soap_call(wsdl=wsdl, method=method)

View File

@ -27,7 +27,6 @@ from xml.etree import ElementTree as etree
import pdfrw
import pdfrw.findobjs
import zeep.exceptions
import zeep.helpers as zeep_helpers
from Cryptodome.Cipher import AES
from django.conf import settings
@ -45,6 +44,7 @@ from passerelle.base.models import BaseResource
from passerelle.base.signature import sign_url
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError, JSONEncoder
from passerelle.utils.soap import SOAPFault
def cartads_file_location(instance, filename):
@ -326,7 +326,7 @@ class AbstractCartaDSCS(BaseResource):
for dossier in CartaDSDossier.objects.filter(cartads_id_dossier__isnull=False, deleted=False):
try:
dossier.cartads_steps_cache = {'steps': self.get_dossier_steps(client, token, dossier)}
except zeep.exceptions.Fault as e:
except SOAPFault as e:
if "n'existe pas" in str(e):
dossier.deleted = True
dossier.save()
@ -336,7 +336,7 @@ class AbstractCartaDSCS(BaseResource):
dossier.cartads_cache_code_acces = client_suivi.service.GetMotPasse(
self.get_token(), dossier.cartads_id_dossier
)
except zeep.exceptions.Fault as e:
except SOAPFault as e:
self.logger.exception('error getting access code (%s) (%s)', dossier.id, e)
try:
infos_dossier = client_dossier.service.GetInfosDossier(
@ -347,7 +347,7 @@ class AbstractCartaDSCS(BaseResource):
dossier.cartads_cache_infos = json.loads(
json.dumps(zeep_helpers.serialize_object(infos_dossier), cls=JSONEncoder)
)
except zeep.exceptions.Fault as e:
except SOAPFault as e:
self.logger.exception('error getting dossier infos (%s) (%s)', dossier.id, e)
dossier.save()
self.sync_subscribers_role(dossier)
@ -951,7 +951,7 @@ class AbstractCartaDSCS(BaseResource):
),
},
)
except zeep.exceptions.Fault as e:
except SOAPFault as e:
self.logger.exception('error pushing file item %d (%s)', piece.id, e)
continue
else:
@ -994,7 +994,7 @@ class AbstractCartaDSCS(BaseResource):
),
},
)
except zeep.exceptions.Fault as e:
except SOAPFault as e:
self.logger.exception('error pushing file item %d (%s)', piece.id, e)
else:
assert resp is None
@ -1047,7 +1047,7 @@ class AbstractCartaDSCS(BaseResource):
'InformationsComplementaires': key_value_of_stringstring(infos),
},
)
except zeep.exceptions.Fault as e:
except SOAPFault as e:
self.logger.exception('error pushing daact file item %d (%s)', piece.id, e)
else:
assert resp is None
@ -1177,7 +1177,7 @@ class AbstractCartaDSCS(BaseResource):
resp = client.service.ActiverServiceSuiviNumerique(
self.get_token(), dossier_number, dossier_password
)
except zeep.exceptions.Fault as e:
except SOAPFault as e:
self.logger.error('error joining dossier %s (%s)', dossier_number, e)
raise APIError('error joining dossier (wrong password?)')
id_dossier = int(resp)

View File

@ -17,7 +17,6 @@
import collections
import zeep
import zeep.exceptions
import zeep.helpers
import zeep.xsd
from django.db import models
@ -127,16 +126,7 @@ class SOAPConnector(BaseResource, HTTPResource):
payload[k] = value[0]
payload.update(post_data or {})
payload = unflatten(payload)
try:
soap_response = getattr(self.client.service, method_name)(**payload)
except zeep.exceptions.Fault as e:
fault_details = {}
for attrib in ['actor', 'code', 'message', 'subcode']:
fault_details[attrib] = getattr(e, attrib, None)
raise APIError('soap:Fault', data=fault_details)
except zeep.exceptions.ValidationError as e:
e.status_code = 400
raise e
soap_response = getattr(self.client.service, method_name)(**payload)
serialized = zeep.helpers.serialize_object(soap_response)
json_response = jsonify(serialized)
return {'err': 0, 'data': json_response}

View File

@ -24,7 +24,6 @@ from django.core.cache import cache
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from zeep.exceptions import Fault as WebFault
from zeep.helpers import serialize_object
from passerelle.base.models import BaseResource
@ -227,12 +226,7 @@ class DPark(BaseResource):
def call(self, operation, *args, **kwargs):
service = get_service(self)
bypass_erroneous_reply = kwargs.pop('bypass_erroneous_reply', False)
try:
reply = getattr(service, operation)(*args, **kwargs)
except (WebFault,) as exc:
raise APIError('ServiceError: %s' % exc)
except (Exception,) as exc:
raise APIError('Error: %s' % exc)
reply = getattr(service, operation)(*args, **kwargs)
reply_code = getattr(reply, 'CodeRetour', None) or getattr(reply, 'Code_Retour', None)
reply_message = getattr(reply, 'MessageRetour', None) or getattr(reply, 'Lib_Retour', None)
if reply_code != '01' and not bypass_erroneous_reply:

View File

@ -76,10 +76,7 @@ class ToulouseMaelis(BaseResource, HTTPResource):
def call(self, wsdl_short_name, service, **kwargs):
client = self.get_client(wsdl_short_name)
method = getattr(client.service, service)
try:
response = method(**kwargs)
except zeep.exceptions.Fault as e:
raise APIError(e.message)
response = method(**kwargs)
return serialize_object(response)
def check_status(self):

View File

@ -21,7 +21,7 @@ from requests import RequestException
from zeep import Client
from zeep.cache import InMemoryCache
from zeep.exceptions import Error as ZeepError
from zeep.exceptions import Fault, TransportError
from zeep.exceptions import Fault, TransportError, ValidationError
from zeep.proxy import OperationProxy, ServiceProxy
from zeep.transports import Transport
@ -62,6 +62,11 @@ class SOAPFault(SOAPError):
)
class SOAPValidationError(SOAPError):
log_error = False
http_status = 400
class OperationProxyWrapper(OperationProxy):
def __call__(self, *args, **kwargs):
client = self._proxy._client
@ -71,6 +76,8 @@ class OperationProxyWrapper(OperationProxy):
raise SOAPServiceUnreachable(client, transport_error)
except Fault as fault:
raise SOAPFault(client, fault)
except ValidationError as validation_error:
raise SOAPValidationError(validation_error)
except ZeepError as zeep_error:
raise SOAPError(str(zeep_error))

View File

@ -5,12 +5,11 @@ from unittest import mock
import pytest
from django.utils.encoding import force_str
from zeep.exceptions import Fault as WebFault
from zeep.exceptions import TransportError
from passerelle.contrib.dpark.models import DPark, Pairing
from passerelle.utils.conversion import to_pdf
from passerelle.utils.jsonresponse import APIError
from passerelle.utils.soap import SOAPError
from tests.utils import ResponsesSoap, make_resource
SLUG = 'test'
@ -71,10 +70,6 @@ class ReplyDataClass(dict):
super().__init__(**kwargs)
class WebFaultHavingLatin1(WebFault):
pass
class MockedService:
def __init__(self, success=True, error_class=None, replydata=None):
self.success = success
@ -82,20 +77,14 @@ class MockedService:
self.replydata = replydata
def raise_error(self):
if self.error_class is WebFault:
raise self.error_class(mock.Mock(faulstring='Error %s raised' % self.error_class.__name__), None)
if self.error_class is TransportError:
raise self.error_class('connection error occured', None)
if self.error_class is WebFaultHavingLatin1:
raise WebFault(message='éêè')
raise Exception('random error')
if self.error_class:
raise self.error_class
def return_response(self, *args, **kwargs):
return ReplyDataClass(**self.replydata)
def __getattr__(self, name):
if self.error_class:
self.raise_error()
self.raise_error()
return self.return_response
@ -106,18 +95,9 @@ def get_service():
def test_call_service_error(dpark, app, get_service):
get_service.return_value = MockedService(error_class=WebFault)
get_service.return_value = MockedService(error_class=SOAPError('boom!'))
resp = app.get('/dpark/test/ping/')
assert 'ServiceError: ' in resp.json['err_desc']
get_service.return_value = MockedService(error_class=TransportError)
resp = app.get('/dpark/test/ping/')
assert 'Error: connection error occured' in resp.json['err_desc']
get_service.return_value = MockedService(error_class=Exception)
resp = app.get('/dpark/test/ping/')
assert 'Error: random error' in resp.json['err_desc']
get_service.return_value = MockedService(error_class=WebFaultHavingLatin1)
resp = app.get('/dpark/test/ping/')
assert 'ServiceError: éêè' in resp.json['err_desc']
assert 'boom!' in resp.json['err_desc']
def test_ping(dpark, app, get_service):

View File

@ -307,7 +307,7 @@ def test_say_hello_method_validation_error(connector, soap, app):
resp = app.get('/soap/test/method/sayHello/')
assert resp.json == {
"err": 1,
"err_class": "passerelle.utils.soap.SOAPError",
"err_class": "passerelle.utils.soap.SOAPValidationError",
"err_desc": soap.VALIDATION_ERROR,
"data": None,
}