Projet

Général

Profil

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

Thomas Noël, 19 septembre 2016 14:20

Télécharger (13,1 ko)

Voir les différences:

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

 tests/test_workflows.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/utilities.py      |   8 +++
 wcs/wf/wscall.py        |  45 ++++++++++++----
 3 files changed, 183 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
    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_app_error = ':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_app_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_app_error = ':stop'
956
    item.perform(formdata)
957
    assert formdata.workflow_data['xxx_status'] == 200
958
    assert formdata.workflow_data['xxx_app_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_app_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_app_error = ':stop'
979
    with pytest.raises(AbortActionException):
980
        item.perform(formdata)
981
    assert formdata.workflow_data['xxx_status'] == 200
982
    assert formdata.workflow_data['xxx_app_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_app_error_code'] == 0
995
    assert formdata.workflow_data['xxx_app_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_app_error_code'] == 1
1007
    assert formdata.workflow_data['xxx_app_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_app_error = ':stop'
1018
    with pytest.raises(AbortActionException):
1019
        item.perform(formdata)
1020
    assert formdata.workflow_data['xxx_status'] == 200
1021
    assert formdata.workflow_data['xxx_app_error_code'] == 1
1022
    assert formdata.workflow_data['xxx_app_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_app_error_code') == 1
1037
    assert formdata.workflow_data.get('xxx_app_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_app_error = ':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_app_error_code') == 1
1051
    assert formdata.workflow_data.get('xxx_app_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
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
-