wscalls: handle connection errors (#53900)

This commit is contained in:
Frédéric Péters 2021-05-10 16:49:57 +02:00
parent d029e24e4c
commit 2ca413d3d3
3 changed files with 70 additions and 12 deletions

View File

@ -19,6 +19,7 @@ from wcs import fields
from wcs.data_sources import NamedDataSource
from wcs.formdef import FormDef
from wcs.qommon.form import UploadedFile
from wcs.qommon.misc import ConnectionError
from wcs.wf.attachment import AddAttachmentWorkflowStatusItem
from wcs.wf.export_to_model import ExportToModel, transform_to_pdf
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
@ -1511,6 +1512,48 @@ def test_formdata_named_wscall_in_conditions(http_requests, pub):
assert len(http_requests.requests) == 1
def test_formdata_named_wscall_in_comment(pub):
create_user(pub)
NamedWsCall.wipe()
wscall = NamedWsCall()
wscall.name = 'Hello world'
wscall.request = {'url': 'http://remote.example.net/json'}
wscall.store()
assert wscall.slug == 'hello_world'
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.CommentField(id='0', label='Hello X{{ webservice.hello_world.foo }}Y.', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
with mock.patch('wcs.qommon.misc._http_request') as mocked_http_request:
data = {'foo': 'bar'}
mocked_http_request.return_value = (mock.Mock(headers={}), 200, json.dumps(data), 'headers')
resp = get_app(pub).get('/test/')
assert 'Hello XbarY.' in resp.text
if pub.loggederror_class:
pub.loggederror_class.wipe()
with mock.patch('wcs.qommon.misc._http_request') as mocked_http_request:
mocked_http_request.side_effect = ConnectionError('...')
resp = get_app(pub).get('/test/')
assert 'Hello XY.' in resp.text
if pub.loggederror_class:
assert pub.loggederror_class.count() == 0
wscall.record_on_errors = True
wscall.store()
resp = get_app(pub).get('/test/')
assert 'Hello XY.' in resp.text
assert pub.loggederror_class.count() == 1
def test_formdata_evolution_registercommenter_to(pub):
user = create_user(pub)

View File

@ -381,6 +381,7 @@ class WebserviceCallStatusItem(WorkflowStatusItem):
post_data=self.post_data,
post_formdata=self.post,
formdata=formdata,
handle_connection_errors=False,
)
except ConnectionError as e:
status = 0

View File

@ -24,6 +24,7 @@ from django.utils.encoding import force_text
from quixote import get_publisher, get_request
from wcs.api_utils import MissingSecret, get_secret_and_orig, sign_url
from wcs.qommon.errors import ConnectionError
from wcs.workflows import WorkflowStatusItem
from .qommon import _, force_str, misc
@ -71,6 +72,7 @@ def call_webservice(
cache=False,
notify_on_errors=False,
record_on_errors=False,
handle_connection_errors=True,
**kwargs,
):
@ -142,18 +144,30 @@ def call_webservice(
formdata_dict['extra'] = payload
payload = formdata_dict
if method in ('PATCH', 'PUT', 'POST'):
if payload:
headers['Content-type'] = 'application/json'
payload = json.dumps(payload, cls=JSONEncoder)
response, status, data, dummy = misc._http_request(url, method=method, body=payload, headers=headers)
elif method == 'DELETE':
response, status, data, dummy = misc._http_request(url, method='DELETE', headers=headers)
else:
response, status, data, dummy = misc.http_get_page(url, headers=headers)
request = get_request()
if cache is True and request and hasattr(request, 'wscalls_cache'):
request.wscalls_cache[unsigned_url] = (status, data)
try:
if method in ('PATCH', 'PUT', 'POST'):
if payload:
headers['Content-type'] = 'application/json'
payload = json.dumps(payload, cls=JSONEncoder)
response, status, data, dummy = misc._http_request(
url, method=method, body=payload, headers=headers
)
elif method == 'DELETE':
response, status, data, dummy = misc._http_request(url, method='DELETE', headers=headers)
else:
response, status, data, dummy = misc.http_get_page(url, headers=headers)
request = get_request()
if cache is True and request and hasattr(request, 'wscalls_cache'):
request.wscalls_cache[unsigned_url] = (status, data)
except ConnectionError as e:
if not handle_connection_errors:
raise e
if notify_on_errors or record_on_errors:
exc_info = sys.exc_info()
get_publisher().notify_of_exception(
exc_info, context='[WSCALL]', notify=notify_on_errors, record=record_on_errors
)
return (None, None, None)
app_error_code = get_app_error_code(response, data, 'json')