wscalls: unify error reporting between wscall objects and actions (#13593) #601
|
@ -235,18 +235,18 @@ def test_webservice_on_error(http_requests, emails, notify_on_errors, record_on_
|
|||
]:
|
||||
msg_mapping = {
|
||||
'400': '400 Bad Request',
|
||||
'400-json': '400 Bad Request (err_desc :(, err_class foo_bar)',
|
||||
'400-json': '400 Bad Request (err_desc: :(, err_class: foo_bar)',
|
||||
'404': '404 Not Found',
|
||||
'404-json': '404 Not Found (err not-found)',
|
||||
'404-json': '404 Not Found',
|
||||
'500': '500 Internal Server Error',
|
||||
'json-err0': None,
|
||||
'json-err0int': None,
|
||||
'json-err1': '(err 1)',
|
||||
'json-err1int': '(err 1)',
|
||||
'json-err1-with-desc': '(err_desc :()',
|
||||
'json-errstr': '(err bug)',
|
||||
'json-errheader1': '(err 1)',
|
||||
'json-errheaderstr': '(err bug)',
|
||||
'json-err1': 'err: 1',
|
||||
'json-err1int': 'err: 1',
|
||||
'json-err1-with-desc': 'err: 1, err_desc: :(',
|
||||
'json-errstr': 'err: bug',
|
||||
'json-errheader1': 'err: 1',
|
||||
'json-errheaderstr': 'err: bug',
|
||||
}
|
||||
wscall.request = {'url': 'http://remote.example.net/%s' % url_part}
|
||||
wscall.store()
|
||||
|
|
|
@ -56,10 +56,10 @@ def test_wscall_record_errors(pub):
|
|||
wscall.perform(formdata)
|
||||
assert len([x for x in formdata.evolution[-1].parts if isinstance(x, JournalWsCallErrorPart)]) == 1
|
||||
assert formdata.evolution[-1].parts[-1].get_json_export_dict() == {
|
||||
'type': 'wscall-error',
|
||||
'summary': '404 Not Found',
|
||||
'label': None,
|
||||
'data': '{"err": 1}',
|
||||
'label': None,
|
||||
'summary': '404 Not Found',
|
||||
'type': 'wscall-error',
|
||||
}
|
||||
|
||||
# error with bytes that can be stored as string
|
||||
|
@ -90,6 +90,47 @@ def test_wscall_record_errors(pub):
|
|||
'data_b64': '8Q==\n',
|
||||
}
|
||||
|
||||
# application error
|
||||
pub.loggederror_class.wipe()
|
||||
with responses.RequestsMock() as rsps:
|
||||
rsps.get('http://test', status=200, body=b'{"err": 1, "err_desc": "some error"}')
|
||||
wscall.perform(formdata)
|
||||
assert formdata.evolution[-1].parts[-1].get_json_export_dict() == {
|
||||
'data': '{"err": 1, "err_desc": "some error"}',
|
||||
'label': None,
|
||||
'summary': 'err: 1, err_desc: some error',
|
||||
'type': 'wscall-error',
|
||||
}
|
||||
assert pub.loggederror_class.count() == 1
|
||||
assert pub.loggederror_class.select()[0].summary == '[WSCALL] err: 1, err_desc: some error'
|
||||
pub.loggederror_class.wipe()
|
||||
|
||||
with responses.RequestsMock() as rsps:
|
||||
rsps.get('http://test', status=200, body=b'{"err": 1}')
|
||||
wscall.perform(formdata)
|
||||
assert formdata.evolution[-1].parts[-1].get_json_export_dict() == {
|
||||
'data': '{"err": 1}',
|
||||
'label': None,
|
||||
'summary': 'err: 1',
|
||||
'type': 'wscall-error',
|
||||
}
|
||||
assert pub.loggederror_class.count() == 1
|
||||
assert pub.loggederror_class.select()[0].summary == '[WSCALL] err: 1'
|
||||
pub.loggederror_class.wipe()
|
||||
|
||||
with responses.RequestsMock() as rsps:
|
||||
rsps.get('http://test', status=200, body=b'xxx', headers={'x-error-code': 'X'})
|
||||
wscall.perform(formdata)
|
||||
assert formdata.evolution[-1].parts[-1].get_json_export_dict() == {
|
||||
'data': 'xxx',
|
||||
'label': None,
|
||||
'summary': 'err: X',
|
||||
'type': 'wscall-error',
|
||||
}
|
||||
assert pub.loggederror_class.count() == 1
|
||||
assert pub.loggederror_class.select()[0].summary == '[WSCALL] err: X'
|
||||
pub.loggederror_class.wipe()
|
||||
|
||||
# error with payload that cannot be converted to JSON
|
||||
pub.loggederror_class.wipe()
|
||||
formdata.evolution[-1].parts = []
|
||||
|
|
|
@ -34,7 +34,7 @@ from wcs.workflows import (
|
|||
WorkflowStatusItem,
|
||||
register_item_class,
|
||||
)
|
||||
from wcs.wscalls import PayloadError, call_webservice, get_app_error_code
|
||||
from wcs.wscalls import PayloadError, call_webservice, get_app_error_code, record_wscall_error
|
||||
|
||||
from ..qommon import _, force_str, pgettext
|
||||
from ..qommon.errors import ConnectionError
|
||||
|
@ -575,20 +575,25 @@ class WebserviceCallStatusItem(WorkflowStatusItem):
|
|||
self.notify_on_errors or self.record_on_errors or self.record_errors
|
||||
):
|
||||
if exception is None:
|
||||
summary = '<no response>'
|
||||
if response is not None:
|
||||
summary = '%s %s' % (response.status_code, response.reason)
|
||||
summary = record_wscall_error(
|
||||
response.status_code,
|
||||
data,
|
||||
response,
|
||||
get_app_error_code(response, data, 'json'),
|
||||
self.notify_on_errors,
|
||||
self.record_on_errors,
|
||||
)
|
||||
else:
|
||||
exc_type, exc_value = sys.exc_info()[:2]
|
||||
summary = traceback.format_exception_only(exc_type, exc_value)[-1]
|
||||
get_publisher().record_error(
|
||||
error_summary=summary,
|
||||
exception=exception,
|
||||
context='[WSCALL]',
|
||||
notify=self.notify_on_errors,
|
||||
record=self.record_on_errors,
|
||||
)
|
||||
|
||||
get_publisher().record_error(
|
||||
error_summary=summary,
|
||||
exception=exception,
|
||||
context='[WSCALL]',
|
||||
notify=self.notify_on_errors,
|
||||
record=self.record_on_errors,
|
||||
)
|
||||
if self.record_errors and formdata.evolution and not recorded_error:
|
||||
formdata.evolution[-1].add_part(JournalWsCallErrorPart(summary, self.label, data))
|
||||
formdata.store()
|
||||
|
|
|
@ -203,6 +203,13 @@ def call_webservice(
|
|||
return (None, None, None)
|
||||
|
||||
app_error_code = get_app_error_code(response, data, 'json')
|
||||
if (app_error_code != 0 or status >= 400) and (notify_on_errors or record_on_errors):
|
||||
record_wscall_error(status, data, response, app_error_code, notify_on_errors, record_on_errors)
|
||||
|
||||
return (response, status, data)
|
||||
|
||||
|
||||
def record_wscall_error(status, data, response, app_error_code, notify_on_errors, record_on_errors):
|
||||
try:
|
||||
json_data = json_loads(data)
|
||||
if not isinstance(json_data, dict):
|
||||
|
@ -210,24 +217,20 @@ def call_webservice(
|
|||
except (ValueError, TypeError):
|
||||
json_data = {}
|
||||
|
||||
if (app_error_code != 0 or status >= 400) and (notify_on_errors or record_on_errors):
|
||||
summary = '<no response>'
|
||||
if response is not None:
|
||||
summary = '%s %s' % (status, response.reason) if status != 200 else ''
|
||||
if app_error_code != 0:
|
||||
details = []
|
||||
for key in ['err_desc', 'err_class']:
|
||||
if json_data.get(key):
|
||||
details.append('%s %s' % (key, json_data[key]))
|
||||
if not details or app_error_code != 1:
|
||||
details.append('err %s' % app_error_code)
|
||||
details = '(%s)' % ', '.join(details)
|
||||
summary = '%s %s' % (summary, details) if summary else details
|
||||
get_publisher().record_error(
|
||||
summary, context='[WSCALL]', notify=notify_on_errors, record=record_on_errors
|
||||
)
|
||||
|
||||
return (response, status, data)
|
||||
summary = '<no response>'
|
||||
if response is not None:
|
||||
summary = '%s %s' % (status, response.reason) if status != 200 else ''
|
||||
if app_error_code != 0:
|
||||
details = [f'err: {app_error_code}'] if status == 200 else []
|
||||
for key in ['err_desc', 'err_class']:
|
||||
if json_data.get(key):
|
||||
details.append('%s: %s' % (key, json_data[key]))
|
||||
details = ', '.join(details) if details else ''
|
||||
summary = '%s (%s)' % (summary, details) if (summary and details) else (summary or details)
|
||||
get_publisher().record_error(
|
||||
summary, context='[WSCALL]', notify=notify_on_errors, record=record_on_errors
|
||||
)
|
||||
return summary
|
||||
|
||||
|
||||
class NamedWsCall(XmlStorableObject):
|
||||
|
|
Loading…
Reference in New Issue