wscalls: add possibility to set a timeout on named calls (#63803)
This commit is contained in:
parent
daf9efebda
commit
a2c825ea2b
|
@ -214,3 +214,26 @@ def test_wscalls_empty_param_values(pub):
|
|||
wscall = NamedWsCall.get('a_new_webservice_call')
|
||||
assert wscall.request['qs_data'] == {'foo': ''}
|
||||
assert wscall.request['post_data'] == {'bar': ''}
|
||||
|
||||
|
||||
def test_wscalls_timeout(pub):
|
||||
create_superuser(pub)
|
||||
NamedWsCall.wipe()
|
||||
app = login(get_app(pub))
|
||||
|
||||
resp = app.get('/backoffice/settings/wscalls/')
|
||||
resp = resp.click('New webservice call')
|
||||
resp.form['name'] = 'a new webservice call'
|
||||
resp.form['description'] = 'description'
|
||||
resp.form['request$timeout'] = 'plop'
|
||||
resp = resp.form.submit('submit')
|
||||
assert resp.pyquery('[data-widget-name="request$timeout"].widget-with-error')
|
||||
assert (
|
||||
resp.pyquery('[data-widget-name="request$timeout"] .error').text()
|
||||
== 'Timeout must be empty or a number.'
|
||||
)
|
||||
resp.form['request$timeout'] = '10'
|
||||
resp = resp.form.submit('submit')
|
||||
|
||||
wscall = NamedWsCall.get('a_new_webservice_call')
|
||||
assert wscall.request['timeout'] == '10'
|
||||
|
|
|
@ -249,3 +249,20 @@ def test_webservice_empty_param_values(http_requests, pub):
|
|||
wscall.call()
|
||||
assert http_requests.get_last('url') == 'http://remote.example.net/json?titi='
|
||||
assert http_requests.get_last('body') == '{"toto": ""}'
|
||||
|
||||
|
||||
def test_webservice_timeout(http_requests, pub):
|
||||
NamedWsCall.wipe()
|
||||
|
||||
wscall = NamedWsCall()
|
||||
wscall.name = 'Hello world'
|
||||
wscall.request = {
|
||||
'method': 'GET',
|
||||
'url': 'http://remote.example.net/connection-error',
|
||||
'timeout': '10',
|
||||
}
|
||||
try:
|
||||
wscall.call()
|
||||
except Exception:
|
||||
pass
|
||||
assert http_requests.get_last('timeout') == 10
|
||||
|
|
|
@ -2875,7 +2875,7 @@ def test_sms_with_passerelle(two_pubs):
|
|||
with mock.patch('wcs.qommon.misc._http_request') as mocked_http_post:
|
||||
mocked_http_post.return_value = (mock.Mock(headers={}), 200, 'data', 'headers')
|
||||
item.perform(formdata)
|
||||
url = mocked_http_post.call_args[0][0]
|
||||
url = mocked_http_post.call_args[1]['url']
|
||||
payload = mocked_http_post.call_args[1]['body']
|
||||
assert 'http://passerelle.example.com' in url
|
||||
assert '?nostop=1' in url
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
</li>
|
||||
{% endif %}
|
||||
<li>{% trans "Method:" %} {% if wscall.request.method == 'POST' %}POST{% else %}GET{% endif %}</li>
|
||||
{% if wscall.request.timeout %}
|
||||
<li>{% trans "Timeout:" %} {{ wscall.request.timeout }}s</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import json
|
|||
import urllib.parse
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_text
|
||||
from quixote import get_publisher, get_request
|
||||
|
||||
|
@ -69,6 +70,7 @@ def call_webservice(
|
|||
post_formdata=None,
|
||||
formdata=None,
|
||||
cache=False,
|
||||
timeout=None,
|
||||
notify_on_errors=False,
|
||||
record_on_errors=False,
|
||||
handle_connection_errors=True,
|
||||
|
@ -145,17 +147,20 @@ def call_webservice(
|
|||
payload = formdata_dict
|
||||
|
||||
try:
|
||||
request_kwargs = {
|
||||
'url': url,
|
||||
'headers': headers,
|
||||
'timeout': int(timeout) if timeout else None,
|
||||
}
|
||||
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
|
||||
)
|
||||
response, status, data, dummy = misc._http_request(method=method, body=payload, **request_kwargs)
|
||||
elif method == 'DELETE':
|
||||
response, status, data, dummy = misc._http_request(url, method='DELETE', headers=headers)
|
||||
response, status, data, dummy = misc._http_request(method='DELETE', **request_kwargs)
|
||||
else:
|
||||
response, status, data, dummy = misc.http_get_page(url, headers=headers)
|
||||
response, status, data, dummy = misc.http_get_page(**request_kwargs)
|
||||
request = get_request()
|
||||
if cache is True and request and hasattr(request, 'wscalls_cache'):
|
||||
request.wscalls_cache[unsigned_url] = (status, data)
|
||||
|
@ -225,7 +230,7 @@ class NamedWsCall(XmlStorableObject):
|
|||
|
||||
def export_request_to_xml(self, element, attribute_name, charset, **kwargs):
|
||||
request = getattr(self, attribute_name)
|
||||
for attr in ('url', 'request_signature_key', 'method'):
|
||||
for attr in ('url', 'request_signature_key', 'method', 'timeout'):
|
||||
ET.SubElement(element, attr).text = force_text(request.get(attr) or '', charset)
|
||||
for attr in ('qs_data', 'post_data'):
|
||||
data_element = ET.SubElement(element, attr)
|
||||
|
@ -238,7 +243,7 @@ class NamedWsCall(XmlStorableObject):
|
|||
|
||||
def import_request_from_xml(self, element, **kwargs):
|
||||
request = {}
|
||||
for attr in ('url', 'request_signature_key', 'method'):
|
||||
for attr in ('url', 'request_signature_key', 'method', 'timeout'):
|
||||
request[attr] = ''
|
||||
if element.find(attr) is not None and element.find(attr).text:
|
||||
request[attr] = force_str(element.find(attr).text)
|
||||
|
@ -359,9 +364,35 @@ class WsCallRequestWidget(CompositeWidget):
|
|||
},
|
||||
)
|
||||
|
||||
def validate_timeout(value):
|
||||
if value and not value.isdecimal():
|
||||
raise ValueError(_('Timeout must be empty or a number.'))
|
||||
|
||||
self.add(
|
||||
StringWidget,
|
||||
'timeout',
|
||||
title=_('Timeout'),
|
||||
value=value.get('timeout'),
|
||||
size=20,
|
||||
hint=_(
|
||||
'Stop waiting for a response after this number of seconds. '
|
||||
'Leave empty to get default timeout (%ss).'
|
||||
)
|
||||
% settings.REQUESTS_TIMEOUT,
|
||||
validation_function=validate_timeout,
|
||||
)
|
||||
|
||||
def _parse(self, request):
|
||||
values = {}
|
||||
for name in ('url', 'request_signature_key', 'qs_data', 'method', 'post_formdata', 'post_data'):
|
||||
for name in (
|
||||
'url',
|
||||
'request_signature_key',
|
||||
'qs_data',
|
||||
'method',
|
||||
'post_formdata',
|
||||
'post_data',
|
||||
'timeout',
|
||||
):
|
||||
if not self.include_post_formdata and name == 'post_formdata':
|
||||
continue
|
||||
value = self.get(name)
|
||||
|
|
Loading…
Reference in New Issue