Skip to content

Commit 63906f0

Browse files
authored
Merge pull request #44 from Yelp/shawn_fix_jsondecode_500
Fix crash caused by a JSONDecodeError
2 parents 293199b + 5310b75 commit 63906f0

File tree

5 files changed

+86
-4
lines changed

5 files changed

+86
-4
lines changed

pyramid_hypernova/batch.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from pyramid_hypernova.rendering import render_blank_markup
99
from pyramid_hypernova.rendering import RenderToken
10+
from pyramid_hypernova.request import ErrorData
1011
from pyramid_hypernova.request import HypernovaQuery
1112
from pyramid_hypernova.request import HypernovaQueryError
1213
from pyramid_hypernova.types import HypernovaError
@@ -141,7 +142,7 @@ def process_responses(self, query, jobs):
141142
__, __, exc_traceback = sys.exc_info()
142143
# We allow clients to send specific error data with their response that they may want to surface.
143144
# Check if any has been propagated, otherwise proceed with generic HypernovaError
144-
if getattr(e, 'error_data', None) is not None:
145+
if isinstance(getattr(e, 'error_data', None), ErrorData):
145146
error = HypernovaError(
146147
name=e.error_data.name,
147148
message=e.error_data.message,

pyramid_hypernova/request.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ def json(self):
101101
response_error_data = self.response.json().get('error', None)
102102
error_data = format_response_error_data(response_error_data)
103103
except JSONDecodeError:
104-
error_data = self.response.text or 'No response data'
104+
error_data = format_response_error_data({
105+
'message': self.response.text or 'SSRS did not return any content',
106+
})
105107

106108
raise HypernovaQueryError(e, error_data)
107109
except ConnectionError as e:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
setup(
66
name='pyramid-hypernova',
7-
version='10.0.1',
7+
version='10.0.2',
88
author='Yelp, Inc.',
99
author_email='[email protected]',
1010
license='MIT',

tests/batch_test.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,84 @@ def test_batch_request_with_unhealthy_service(
365365
),
366366
}
367367

368+
def test_batch_request_with_unhealthy_service_and_none_error_data(
369+
self,
370+
spy_plugin_controller,
371+
test_data,
372+
batch_request,
373+
mock_hypernova_query,
374+
):
375+
data = test_data[0]
376+
job = Job(name='MyComponent.js', data=data[0], context={})
377+
token = batch_request.render('MyComponent.js', data[0])
378+
379+
mock_hypernova_query.return_value.json.side_effect = HypernovaQueryError(
380+
'oh no',
381+
None,
382+
)
383+
response = batch_request.submit()
384+
385+
if batch_request.max_batch_size is None:
386+
assert mock_hypernova_query.call_count == 1
387+
else:
388+
# Division (rounded-up) up to get total number of calls
389+
jobs_count = len(batch_request.jobs)
390+
max_batch_size = batch_request.max_batch_size
391+
batch_count = (jobs_count + (max_batch_size - 1)) // max_batch_size
392+
assert mock_hypernova_query.call_count == batch_count
393+
mock_hypernova_query.assert_called_with(mock.ANY, mock.ANY, mock.ANY, batch_count == 1, {})
394+
395+
assert response == {
396+
token.identifier: JobResult(
397+
error=HypernovaError(
398+
name='HypernovaQueryError',
399+
message='oh no',
400+
stack=mock.ANY,
401+
),
402+
html=render_blank_markup(token.identifier, job, True, batch_request.json_encoder),
403+
job=job,
404+
),
405+
}
406+
407+
def test_batch_request_with_unhealthy_service_and_other_type_error_data(
408+
self,
409+
spy_plugin_controller,
410+
test_data,
411+
batch_request,
412+
mock_hypernova_query,
413+
):
414+
data = test_data[0]
415+
job = Job(name='MyComponent.js', data=data[0], context={})
416+
token = batch_request.render('MyComponent.js', data[0])
417+
418+
mock_hypernova_query.return_value.json.side_effect = HypernovaQueryError(
419+
'oh no',
420+
'i should not be a string',
421+
)
422+
response = batch_request.submit()
423+
424+
if batch_request.max_batch_size is None:
425+
assert mock_hypernova_query.call_count == 1
426+
else:
427+
# Division (rounded-up) up to get total number of calls
428+
jobs_count = len(batch_request.jobs)
429+
max_batch_size = batch_request.max_batch_size
430+
batch_count = (jobs_count + (max_batch_size - 1)) // max_batch_size
431+
assert mock_hypernova_query.call_count == batch_count
432+
mock_hypernova_query.assert_called_with(mock.ANY, mock.ANY, mock.ANY, batch_count == 1, {})
433+
434+
assert response == {
435+
token.identifier: JobResult(
436+
error=HypernovaError(
437+
name='HypernovaQueryError',
438+
message='oh no',
439+
stack=mock.ANY,
440+
),
441+
html=render_blank_markup(token.identifier, job, True, batch_request.json_encoder),
442+
job=job,
443+
),
444+
}
445+
368446

369447
class TestBatchRequestLifecycleMethods:
370448
"""Test that BatchRequest calls plugin lifecycle methods at the

tests/request_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ def test_json_error_fallbacks_to_text(self, mock_fido_fetch, mock_requests_post)
126126
data=mock.ANY,
127127
)
128128
assert str(exc_info.value) == str(HypernovaQueryError(HTTPError('ayy lmao')))
129-
assert exc_info.value.error_data == 'Non-JSON Error Body'
129+
assert isinstance(exc_info.value.error_data, ErrorData)
130+
assert exc_info.value.error_data.message == 'Non-JSON Error Body'
130131

131132
def test_successful_send_asynchronous(self, mock_fido_fetch, mock_requests_post):
132133
mock_fido_fetch.return_value.wait.return_value.code = 200

0 commit comments

Comments
 (0)