Projet

Général

Profil

0001-wscall-add-action-if-err-in-json-data-or-specific-he.patch

Thomas Noël, 06 septembre 2016 15:14

Télécharger (13 ko)

Voir les différences:

Subject: [PATCH] wscall: add action if err in json data or specific header
 (#12916)

 tests/test_workflows.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/utilities.py      |   8 +++
 wcs/wf/wscall.py        |  43 +++++++++++----
 3 files changed, 182 insertions(+), 9 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_error_code = ':stop'
930
    with pytest.raises(AbortActionException):
931
        item.perform(formdata)
932

  
933
    item = WebserviceCallStatusItem()
934
    item.url = 'http://remote.example.net/json-errheader1'
935
    item.post = False
936
    item.action_on_error_code = ':stop'
937
    with pytest.raises(AbortActionException):
938
        item.perform(formdata)
939

  
940
    item = WebserviceCallStatusItem()
941
    item.url = 'http://remote.example.net/json-err0'
942
    item.post = False
943
    item.varname = 'xxx'
944
    item.perform(formdata)
945
    assert formdata.workflow_data['xxx_status'] == 200
946
    assert formdata.workflow_data['xxx_error_code'] == 0
947
    assert formdata.workflow_data['xxx_response'] == {'data': 'foo', 'err': 0}
948
    assert formdata.workflow_data.get('xxx_time')
949
    formdata.workflow_data = None
950

  
951
    item = WebserviceCallStatusItem()
952
    item.url = 'http://remote.example.net/json-err0'
953
    item.post = False
954
    item.varname = 'xxx'
955
    item.action_on_error_code = ':stop'
956
    item.perform(formdata)
957
    assert formdata.workflow_data['xxx_status'] == 200
958
    assert formdata.workflow_data['xxx_error_code'] == 0
959
    assert formdata.workflow_data['xxx_response'] == {'data': 'foo', 'err': 0}
960
    assert formdata.workflow_data.get('xxx_time')
961
    formdata.workflow_data = None
962

  
963
    item = WebserviceCallStatusItem()
964
    item.url = 'http://remote.example.net/json-err1'
965
    item.post = False
966
    item.varname = 'xxx'
967
    item.perform(formdata)
968
    assert formdata.workflow_data['xxx_status'] == 200
969
    assert formdata.workflow_data['xxx_error_code'] == 1
970
    assert 'xxx_response' not in formdata.workflow_data
971
    assert formdata.workflow_data.get('xxx_time')
972
    formdata.workflow_data = None
973

  
974
    item = WebserviceCallStatusItem()
975
    item.url = 'http://remote.example.net/json-err1'
976
    item.post = False
977
    item.varname = 'xxx'
978
    item.action_on_error_code = ':stop'
979
    with pytest.raises(AbortActionException):
980
        item.perform(formdata)
981
    assert formdata.workflow_data['xxx_status'] == 200
982
    assert formdata.workflow_data['xxx_error_code'] == 1
983
    assert formdata.workflow_data['xxx_error_response'] == {'data': '', 'err': 1}
984
    assert 'xxx_response' not in formdata.workflow_data
985
    assert formdata.workflow_data.get('xxx_time')
986
    formdata.workflow_data = None
987

  
988
    item = WebserviceCallStatusItem()
989
    item.url = 'http://remote.example.net/json-errheader0'
990
    item.post = False
991
    item.varname = 'xxx'
992
    item.perform(formdata)
993
    assert formdata.workflow_data['xxx_status'] == 200
994
    assert formdata.workflow_data['xxx_error_code'] == 0
995
    assert formdata.workflow_data['xxx_error_header'] == '0'
996
    assert formdata.workflow_data['xxx_response'] == {'foo': 'bar'}
997
    assert formdata.workflow_data.get('xxx_time')
998
    formdata.workflow_data = None
999

  
1000
    item = WebserviceCallStatusItem()
1001
    item.url = 'http://remote.example.net/json-errheader1'
1002
    item.post = False
1003
    item.varname = 'xxx'
1004
    item.perform(formdata)
1005
    assert formdata.workflow_data['xxx_status'] == 200
1006
    assert formdata.workflow_data['xxx_error_code'] == 1
1007
    assert formdata.workflow_data['xxx_error_header'] == '1'
1008
    assert formdata.workflow_data['xxx_error_response'] == {'foo': 'bar'}
1009
    assert 'xxx_response' not in formdata.workflow_data
1010
    assert formdata.workflow_data.get('xxx_time')
1011
    formdata.workflow_data = None
1012

  
1013
    item = WebserviceCallStatusItem()
1014
    item.url = 'http://remote.example.net/json-errheader1'
1015
    item.post = False
1016
    item.varname = 'xxx'
1017
    item.action_on_error_code = ':stop'
1018
    with pytest.raises(AbortActionException):
1019
        item.perform(formdata)
1020
    assert formdata.workflow_data['xxx_status'] == 200
1021
    assert formdata.workflow_data['xxx_error_code'] == 1
1022
    assert formdata.workflow_data['xxx_error_header'] == '1'
1023
    assert formdata.workflow_data['xxx_error_response'] == {'foo': 'bar'}
1024
    assert 'xxx_response' not in formdata.workflow_data
1025
    assert formdata.workflow_data.get('xxx_time')
1026
    formdata.workflow_data = None
1027

  
1028
    item = WebserviceCallStatusItem()
1029
    item.url = 'http://remote.example.net/xml-errheader'
1030
    item.post = False
1031
    item.varname = 'xxx'
1032
    item.response_type = 'attachment'
1033
    item.record_errors = True
1034
    item.perform(formdata)
1035
    assert formdata.workflow_data.get('xxx_status') == 200
1036
    assert formdata.workflow_data.get('xxx_error_code') == 1
1037
    assert formdata.workflow_data.get('xxx_error_header') == '1'
1038
    assert 'xxx_response' not in formdata.workflow_data
1039

  
1040
    item = WebserviceCallStatusItem()
1041
    item.url = 'http://remote.example.net/xml-errheader'
1042
    item.post = False
1043
    item.varname = 'xxx'
1044
    item.response_type = 'attachment'
1045
    item.record_errors = True
1046
    item.action_on_error_code = ':stop'
1047
    with pytest.raises(AbortActionException):
1048
        item.perform(formdata)
1049
    assert formdata.workflow_data.get('xxx_status') == 200
1050
    assert formdata.workflow_data.get('xxx_error_code') == 1
1051
    assert formdata.workflow_data.get('xxx_error_header') == '1'
1052
    assert 'xxx_response' not in formdata.workflow_data
913 1053

  
914 1054
def test_timeout(pub):
915 1055
    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-errheader0': (200, '{"foo": "bar"}',
256
                                              {'x-error-code': '0'}),
257
            'http://remote.example.net/json-errheader1': (200, '{"foo": "bar"}',
258
                                              {'x-error-code': '1'}),
253 259
            'http://remote.example.net/xml': (200, '<?xml version="1.0"><foo/>',
254 260
                                              {'content-type': 'text/xml'}),
261
            'http://remote.example.net/xml-errheader': (200, '<?xml version="1.0"><foo/>',
262
                                              {'content-type': 'text/xml', 'x-error-code': '1'}),
255 263
            }.get(base_url, (200, '', {}))
256 264

  
257 265
        class FakeResponse(object):
wcs/wf/wscall.py
109 109
    action_on_5xx = ':stop'
110 110
    action_on_bad_data = ':pass'
111 111
    action_on_network_errors = ':stop'
112
    action_on_error_code = ':pass'
112 113
    notify_on_errors = True
113 114
    record_errors = False
114 115

  
......
133 134
    def get_parameters(self):
134 135
        return ('url', 'post', 'varname', 'request_signature_key', 'post_data',
135 136
                'action_on_4xx', 'action_on_5xx', 'action_on_bad_data',
136
                'action_on_network_errors', 'notify_on_errors',
137
                'action_on_network_errors', 'action_on_error_code', 'notify_on_errors',
137 138
                'record_errors', 'label', 'method', 'response_type',
138 139
                'qs_data')
139 140

  
......
199 200
        error_actions.extend([(x.id, _('Jump to %s') % x.name) for x in
200 201
            self.parent.parent.possible_status])
201 202
        for attribute in ('action_on_4xx', 'action_on_5xx', 'action_on_network_errors',
202
                'action_on_bad_data'):
203
                'action_on_bad_data', 'action_on_error_code'):
203 204
            if not attribute in parameters:
204 205
                continue
205 206
            if attribute == 'action_on_bad_data':
......
213 214
                    'action_on_4xx': _('Action on HTTP error 4xx'),
214 215
                    'action_on_5xx': _('Action on HTTP error 5xx'),
215 216
                    'action_on_bad_data': _('Action on non-JSON response'),
216
                    'action_on_network_errors': _('Action on network errors')
217
                    'action_on_network_errors': _('Action on network errors'),
218
                    'action_on_error_code': _('Action on JSON or header error'),
217 219
                    }.get(attribute)
218 220
            form.add(SingleSelectWidget, '%s%s' % (prefix, attribute),
219 221
                    title=label,
......
249 251
            self.action_on_error(self.action_on_network_errors, formdata,
250 252
                    exc_info=sys.exc_info())
251 253

  
254
        error_code = 0
255
        error_code_header = response.getheader('x-error-code')
256
        if error_code_header:
257
            # result is good only if header value is '0'
258
            try:
259
                error_code = int(error_code_header)
260
            except ValueError as e:
261
                error_code = 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
                    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_error_code' % self.varname: error_code,
256 276
            }
277
            if error_code_header:
278
                workflow_data['%s_error_header' % self.varname] = 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 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 error_code != 0:
294
            self.action_on_error(self.action_on_error_code, 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:
......
332 357
    def get_target_status(self):
333 358
        targets = []
334 359
        for attribute in ('action_on_4xx', 'action_on_5xx', 'action_on_bad_data',
335
                'action_on_network_errors'):
360
                'action_on_network_errors', 'action_on_error_code'):
336 361
            value = getattr(self, attribute)
337 362
            if value in (':pass', ':stop'):
338 363
                continue
339
-