Projet

Général

Profil

0001-wscall-add-action-if-error-in-application-12916.patch

Thomas Noël, 03 octobre 2016 12:52

Télécharger (16,1 ko)

Voir les différences:

Subject: [PATCH] wscall: add action if error in application (#12916)

 tests/test_workflows.py | 212 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/utilities.py      |  11 +++
 wcs/wf/wscall.py        |  45 +++++++---
 3 files changed, 258 insertions(+), 10 deletions(-)
tests/test_workflows.py
910 910
    assert qs['evalme'] == [formdata.get_display_id()]
911 911
    assert qs['str'] == ['abcd']
912 912

  
913
def test_webservice_call_error_handling(pub):
914
    pub.substitutions.feed(MockSubstitutionVariables())
915

  
916
    FormDef.wipe()
917
    formdef = FormDef()
918
    formdef.name = 'baz'
919
    formdef.fields = []
920
    formdef.store()
921

  
922
    formdata = formdef.data_class()()
923
    formdata.just_created()
924
    formdata.store()
925

  
926
    item = WebserviceCallStatusItem()
927
    item.url = 'http://remote.example.net/json-err1'
928
    item.post = False
929
    item.action_on_app_error = ':stop'
930
    item.action_on_4xx = ':pass'
931
    item.action_on_5xx = ':pass'
932
    item.action_on_network_errors = ':pass'
933
    with pytest.raises(AbortActionException):
934
        item.perform(formdata)
935

  
936
    item = WebserviceCallStatusItem()
937
    item.url = 'http://remote.example.net/json-errheader1'
938
    item.post = False
939
    item.action_on_app_error = ':stop'
940
    item.action_on_4xx = ':pass'
941
    item.action_on_5xx = ':pass'
942
    item.action_on_network_errors = ':pass'
943
    with pytest.raises(AbortActionException):
944
        item.perform(formdata)
945

  
946
    item = WebserviceCallStatusItem()
947
    item.url = 'http://remote.example.net/json-errheaderstr'
948
    item.post = False
949
    item.action_on_app_error = ':stop'
950
    item.action_on_4xx = ':pass'
951
    item.action_on_5xx = ':pass'
952
    item.action_on_network_errors = ':pass'
953
    with pytest.raises(AbortActionException):
954
        item.perform(formdata)
955

  
956
    item = WebserviceCallStatusItem()
957
    item.url = 'http://remote.example.net/json-err0'
958
    item.post = False
959
    item.varname = 'xxx'
960
    item.perform(formdata)
961
    assert formdata.workflow_data['xxx_status'] == 200
962
    assert formdata.workflow_data['xxx_app_error_code'] == 0
963
    assert formdata.workflow_data['xxx_response'] == {'data': 'foo', 'err': 0}
964
    assert formdata.workflow_data.get('xxx_time')
965
    formdata.workflow_data = None
966

  
967
    item = WebserviceCallStatusItem()
968
    item.url = 'http://remote.example.net/json-err0'
969
    item.post = False
970
    item.varname = 'xxx'
971
    item.action_on_app_error = ':stop'
972
    item.perform(formdata)
973
    assert formdata.workflow_data['xxx_status'] == 200
974
    assert formdata.workflow_data['xxx_app_error_code'] == 0
975
    assert formdata.workflow_data['xxx_response'] == {'data': 'foo', 'err': 0}
976
    assert formdata.workflow_data.get('xxx_time')
977
    formdata.workflow_data = None
978

  
979
    item = WebserviceCallStatusItem()
980
    item.url = 'http://remote.example.net/json-err1'
981
    item.post = False
982
    item.varname = 'xxx'
983
    item.perform(formdata)
984
    assert formdata.workflow_data['xxx_status'] == 200
985
    assert formdata.workflow_data['xxx_app_error_code'] == 1
986
    assert 'xxx_response' not in formdata.workflow_data
987
    assert formdata.workflow_data.get('xxx_time')
988
    formdata.workflow_data = None
989

  
990
    item = WebserviceCallStatusItem()
991
    item.url = 'http://remote.example.net/json-errstr'
992
    item.post = False
993
    item.varname = 'xxx'
994
    item.perform(formdata)
995
    assert formdata.workflow_data['xxx_status'] == 200
996
    assert formdata.workflow_data['xxx_app_error_code'] == 'bug'
997
    assert 'xxx_response' not in formdata.workflow_data
998
    assert formdata.workflow_data.get('xxx_time')
999
    formdata.workflow_data = None
1000

  
1001
    item = WebserviceCallStatusItem()
1002
    item.url = 'http://remote.example.net/json-err1'
1003
    item.post = False
1004
    item.varname = 'xxx'
1005
    item.action_on_app_error = ':stop'
1006
    with pytest.raises(AbortActionException):
1007
        item.perform(formdata)
1008
    assert formdata.workflow_data['xxx_status'] == 200
1009
    assert formdata.workflow_data['xxx_app_error_code'] == 1
1010
    assert formdata.workflow_data['xxx_error_response'] == {'data': '', 'err': 1}
1011
    assert 'xxx_response' not in formdata.workflow_data
1012
    assert formdata.workflow_data.get('xxx_time')
1013
    formdata.workflow_data = None
1014

  
1015
    item = WebserviceCallStatusItem()
1016
    item.url = 'http://remote.example.net/json-errheader0'
1017
    item.post = False
1018
    item.varname = 'xxx'
1019
    item.perform(formdata)
1020
    assert formdata.workflow_data['xxx_status'] == 200
1021
    assert formdata.workflow_data['xxx_app_error_code'] == 0
1022
    assert formdata.workflow_data['xxx_app_error_header'] == '0'
1023
    assert formdata.workflow_data['xxx_response'] == {'foo': 'bar'}
1024
    assert formdata.workflow_data.get('xxx_time')
1025
    formdata.workflow_data = None
1026

  
1027
    item = WebserviceCallStatusItem()
1028
    item.url = 'http://remote.example.net/json-errheader1'
1029
    item.post = False
1030
    item.varname = 'xxx'
1031
    item.perform(formdata)
1032
    assert formdata.workflow_data['xxx_status'] == 200
1033
    assert formdata.workflow_data['xxx_app_error_code'] == 1
1034
    assert formdata.workflow_data['xxx_app_error_header'] == '1'
1035
    assert formdata.workflow_data['xxx_error_response'] == {'foo': 'bar'}
1036
    assert 'xxx_response' not in formdata.workflow_data
1037
    assert formdata.workflow_data.get('xxx_time')
1038
    formdata.workflow_data = None
1039

  
1040
    item = WebserviceCallStatusItem()
1041
    item.url = 'http://remote.example.net/json-errheaderstr'
1042
    item.post = False
1043
    item.varname = 'xxx'
1044
    item.perform(formdata)
1045
    assert formdata.workflow_data['xxx_status'] == 200
1046
    assert formdata.workflow_data['xxx_app_error_code'] == 'bug'
1047
    assert formdata.workflow_data['xxx_app_error_header'] == 'bug'
1048
    assert formdata.workflow_data['xxx_error_response'] == {'foo': 'bar'}
1049
    assert 'xxx_response' not in formdata.workflow_data
1050
    assert formdata.workflow_data.get('xxx_time')
1051
    formdata.workflow_data = None
1052

  
1053
    item = WebserviceCallStatusItem()
1054
    item.url = 'http://remote.example.net/json-errheader1'
1055
    item.post = False
1056
    item.varname = 'xxx'
1057
    item.action_on_app_error = ':stop'
1058
    with pytest.raises(AbortActionException):
1059
        item.perform(formdata)
1060
    assert formdata.workflow_data['xxx_status'] == 200
1061
    assert formdata.workflow_data['xxx_app_error_code'] == 1
1062
    assert formdata.workflow_data['xxx_app_error_header'] == '1'
1063
    assert formdata.workflow_data['xxx_error_response'] == {'foo': 'bar'}
1064
    assert 'xxx_response' not in formdata.workflow_data
1065
    assert formdata.workflow_data.get('xxx_time')
1066
    formdata.workflow_data = None
1067

  
1068
    item = WebserviceCallStatusItem()
1069
    item.url = 'http://remote.example.net/xml-errheader'
1070
    item.post = False
1071
    item.varname = 'xxx'
1072
    item.response_type = 'attachment'
1073
    item.record_errors = True
1074
    item.perform(formdata)
1075
    assert formdata.workflow_data.get('xxx_status') == 200
1076
    assert formdata.workflow_data.get('xxx_app_error_code') == 1
1077
    assert formdata.workflow_data.get('xxx_app_error_header') == '1'
1078
    assert 'xxx_response' not in formdata.workflow_data
1079
    formdata.workflow_data = None
1080

  
1081
    item = WebserviceCallStatusItem()
1082
    item.url = 'http://remote.example.net/xml-errheader'
1083
    item.post = False
1084
    item.varname = 'xxx'
1085
    item.response_type = 'attachment'
1086
    item.record_errors = True
1087
    item.action_on_app_error = ':stop'
1088
    with pytest.raises(AbortActionException):
1089
        item.perform(formdata)
1090
    assert formdata.workflow_data.get('xxx_status') == 200
1091
    assert formdata.workflow_data.get('xxx_app_error_code') == 1
1092
    assert formdata.workflow_data.get('xxx_app_error_header') == '1'
1093
    assert 'xxx_response' not in formdata.workflow_data
1094
    formdata.workflow_data = None
1095

  
1096
    item = WebserviceCallStatusItem()
1097
    item.url = 'http://remote.example.net/xml-errheader'
1098
    item.post = False
1099
    item.varname = 'xxx'
1100
    item.response_type = 'json' # wait for json but receive xml
1101
    item.record_errors = True
1102
    item.perform(formdata)
1103
    assert formdata.workflow_data.get('xxx_status') == 200
1104
    assert formdata.workflow_data.get('xxx_app_error_code') == 1
1105
    assert formdata.workflow_data.get('xxx_app_error_header') == '1'
1106
    assert 'xxx_response' not in formdata.workflow_data
1107
    formdata.workflow_data = None
1108

  
1109
    item = WebserviceCallStatusItem()
1110
    item.url = 'http://remote.example.net/json-err1'
1111
    item.post = False
1112
    item.action_on_app_error = ':stop'
1113
    item.response_type = 'attachment' # err value is not an error
1114
    item.perform(formdata)            # so, everything is "ok" here
1115
    formdata.workflow_data = None
1116

  
1117
    item = WebserviceCallStatusItem()
1118
    item.url = 'http://remote.example.net/json-errheaderstr'
1119
    item.post = False
1120
    item.action_on_app_error = ':stop'
1121
    item.response_type = 'attachment'
1122
    with pytest.raises(AbortActionException):
1123
        item.perform(formdata)
1124
    formdata.workflow_data = None
913 1125

  
914 1126
def test_timeout(pub):
915 1127
    workflow = Workflow(name='timeout')
tests/utilities.py
250 250
            'http://remote.example.net/404-json': (404, '{"err": 1}', None),
251 251
            'http://remote.example.net/500': (500, 'internal server error', None),
252 252
            'http://remote.example.net/json': (200, '{"foo": "bar"}', None),
253
            'http://remote.example.net/json-err0': (200, '{"data": "foo", "err": 0}', None),
254
            'http://remote.example.net/json-err1': (200, '{"data": "", "err": 1}', None),
255
            'http://remote.example.net/json-errstr': (200, '{"data": "", "err": "bug"}', None),
256
            'http://remote.example.net/json-errheader0': (200, '{"foo": "bar"}',
257
                                              {'x-error-code': '0'}),
258
            'http://remote.example.net/json-errheader1': (200, '{"foo": "bar"}',
259
                                              {'x-error-code': '1'}),
260
            'http://remote.example.net/json-errheaderstr': (200, '{"foo": "bar"}',
261
                                              {'x-error-code': 'bug'}),
253 262
            'http://remote.example.net/xml': (200, '<?xml version="1.0"><foo/>',
254 263
                                              {'content-type': 'text/xml'}),
264
            'http://remote.example.net/xml-errheader': (200, '<?xml version="1.0"><foo/>',
265
                                              {'content-type': 'text/xml', 'x-error-code': '1'}),
255 266
            }.get(base_url, (200, '', {}))
256 267

  
257 268
        class FakeResponse(object):
wcs/wf/wscall.py
105 105
    _method = None
106 106
    response_type = 'json'
107 107

  
108
    action_on_app_error = ':pass'
108 109
    action_on_4xx = ':stop'
109 110
    action_on_5xx = ':stop'
110 111
    action_on_bad_data = ':pass'
......
132 133

  
133 134
    def get_parameters(self):
134 135
        return ('url', 'post', 'varname', 'request_signature_key', 'post_data',
135
                'action_on_4xx', 'action_on_5xx', 'action_on_bad_data',
136
                'action_on_app_error', 'action_on_4xx', 'action_on_5xx', 'action_on_bad_data',
136 137
                'action_on_network_errors', 'notify_on_errors',
137 138
                'record_errors', 'label', 'method', 'response_type',
138 139
                'qs_data')
......
198 199
        error_actions = [(':stop', _('Stop')), (':pass', _('Ignore'))]
199 200
        error_actions.extend([(x.id, _('Jump to %s') % x.name) for x in
200 201
            self.parent.parent.possible_status])
201
        for attribute in ('action_on_4xx', 'action_on_5xx', 'action_on_network_errors',
202
                'action_on_bad_data'):
202
        for attribute in ('action_on_app_error', 'action_on_4xx', 'action_on_5xx',
203
                          'action_on_network_errors', 'action_on_bad_data'):
203 204
            if not attribute in parameters:
204 205
                continue
205 206
            if attribute == 'action_on_bad_data':
......
210 211
            else:
211 212
                attrs = {}
212 213
            label = {
214
                    'action_on_app_error': _('Action on application error'),
213 215
                    'action_on_4xx': _('Action on HTTP error 4xx'),
214 216
                    'action_on_5xx': _('Action on HTTP error 5xx'),
215 217
                    'action_on_bad_data': _('Action on non-JSON response'),
......
249 251
            self.action_on_error(self.action_on_network_errors, formdata,
250 252
                    exc_info=sys.exc_info())
251 253

  
254
        app_error_code = 0
255
        app_error_code_header = response.getheader('x-error-code')
256
        if app_error_code_header:
257
            # result is good only if header value is '0'
258
            try:
259
                app_error_code = int(app_error_code_header)
260
            except ValueError as e:
261
                app_error_code = app_error_code_header
262
        elif self.response_type == 'json':
263
            try:
264
                d = json_loads(data)
265
            except (ValueError, TypeError) as e:
266
                pass
267
            else:
268
                if isinstance(d, dict) and d.get('err'):
269
                    app_error_code = d['err']
270

  
252 271
        if self.varname:
253 272
            workflow_data = {
254 273
                '%s_status' % self.varname: status,
255 274
                '%s_time' % self.varname: datetime.datetime.now().isoformat(),
275
                '%s_app_error_code' % self.varname: app_error_code,
256 276
            }
277
            if app_error_code_header:
278
                workflow_data['%s_app_error_header' % self.varname] = app_error_code_header
257 279
            if status in (204, 205):
258 280
                pass # not returning any content
259
            elif (status // 100) == 2:
281
            elif (status // 100) == 2 and app_error_code == 0:
260 282
                self.store_response(formdata, response, data, workflow_data)
261 283
            else: # on error, record data if it is JSON
262 284
                try:
......
268 290
            formdata.update_workflow_data(workflow_data)
269 291
            formdata.store()
270 292

  
293
        if app_error_code != 0:
294
            self.action_on_error(self.action_on_app_error, formdata, response, data=data)
271 295
        if (status // 100) == 4:
272 296
            self.action_on_error(self.action_on_4xx, formdata, response, data=data)
273 297
        if (status // 100) == 5:
......
284 308
                        response, data=data, exc_info=sys.exc_info())
285 309
            else:
286 310
                workflow_data['%s_response' % self.varname] = d
287
                if isinstance(d.get('data'), dict) and d['data'].get('display_id'):
288
                    formdata.id_display = d.get('data', {}).get('display_id')
289
                elif d.get('display_id'):
290
                    formdata.id_display = d.get('display_id')
311
                if isinstance(d, dict):
312
                    if isinstance(d.get('data'), dict) and d['data'].get('display_id'):
313
                        formdata.id_display = d.get('data', {}).get('display_id')
314
                    elif d.get('display_id'):
315
                        formdata.id_display = d.get('display_id')
291 316
        else: # store result as attachment
292 317
            content_type = response.getheader('content-type') or ''
293 318
            if content_type:
......
331 356

  
332 357
    def get_target_status(self):
333 358
        targets = []
334
        for attribute in ('action_on_4xx', 'action_on_5xx', 'action_on_bad_data',
335
                'action_on_network_errors'):
359
        for attribute in ('action_on_app_error', 'action_on_4xx', 'action_on_5xx',
360
                          'action_on_bad_data', 'action_on_network_errors'):
336 361
            value = getattr(self, attribute)
337 362
            if value in (':pass', ':stop'):
338 363
                continue
339
-