json cell: add possibility to retrieve data from multiple URLs (#17185)

ex:
  "metro": {
    "url": "https://.../stops?identifier=[identifier]",
    "additional-data": [
       {"key": "schedule",
        "url": "https://.../schedule?stop_identifier=[identifier]"
       }
    ]
  }
This commit is contained in:
Frédéric Péters 2017-06-26 10:16:37 +02:00
parent 4b140eb372
commit 88052430dd
2 changed files with 91 additions and 20 deletions

View File

@ -832,6 +832,14 @@ class JsonCellBase(CellBase):
varnames = None
force_async = False
actions = {}
additional_data = None
# [
# {'key': ...,
# 'url': ...,
# 'cache_duration': ... (optional)
# },
# ...
# ]
_json_content = None
@ -848,28 +856,45 @@ class JsonCellBase(CellBase):
if varname in context['request'].GET and varname not in context:
context[varname] = context['request'].GET[varname]
self._json_content = None
extra_context['json'] = None
try:
url = utils.get_templated_url(self.url, context)
except utils.UnknownTemplateVariableError:
logger = logging.getLogger(__name__)
logger.warning('unknown variable in template URL (%s)', self.url)
return extra_context
json_response = utils.requests.get(url,
headers={'Accept': 'application/json'},
remote_service='auto',
cache_duration=self.cache_duration,
without_user=True,
raise_if_not_cached=not(context.get('synchronous')),
invalidate_cache=invalidate_cache,
)
if json_response.status_code == 200:
data_urls = [{'key': 'json', 'url': self.url, 'cache_duration': self.cache_duration}]
data_urls.extend(self.additional_data or [])
for data_url_dict in data_urls:
extra_context[data_url_dict['key']] = None
for data_url_dict in data_urls:
data_key = data_url_dict['key']
try:
self._json_content = json.loads(json_response.content)
except ValueError:
url = utils.get_templated_url(data_url_dict['url'], context)
except utils.UnknownTemplateVariableError:
logger = logging.getLogger(__name__)
logger.error('invalid json content (%s)', url)
extra_context['json'] = self._json_content
logger.warning('unknown variable in template URL (%s)', self.url)
continue
json_response = utils.requests.get(url,
headers={'Accept': 'application/json'},
remote_service='auto',
cache_duration=data_url_dict.get('cache_duration', self.cache_duration),
without_user=True,
raise_if_not_cached=not(context.get('synchronous')),
invalidate_cache=invalidate_cache,
)
if json_response.status_code == 200:
try:
extra_context[data_key] = json.loads(json_response.content)
except ValueError:
logger = logging.getLogger(__name__)
logger.error('invalid json content (%s)', url)
continue
# update context with data key so it can be used in future
# templated URLs
context[data_key] = extra_context[data_key]
# keep cache of first response as it may be used to find the
# appropriate template.
self._json_content = extra_context['json']
return extra_context
@property
@ -1007,6 +1032,10 @@ class ConfigJsonCell(JsonCellBase):
return settings.JSON_CELL_TYPES[self.key].get('actions',
JsonCellBase.actions)
@property
def additional_data(self):
return settings.JSON_CELL_TYPES[self.key].get('additional-data')
@property
def template_name(self):
return 'combo/json/%s.html' % self.key

View File

@ -364,3 +364,45 @@ def test_json_force_async():
assert cell.render(Context({'synchronous': True})) == 'world2'
# rerun with stuff in cache
assert cell.render(Context({})) == 'world2'
def test_config_json_cell_additional_url(app):
page = Page(title='example page', slug='index')
page.save()
with override_settings(JSON_CELL_TYPES={
'test-config-json-cell-2': {
'name': 'Foobar',
'url': 'http://foo',
'additional-data': [
{'key': 'plop', 'url': 'http://bar'},
]
}},
TEMPLATE_DIRS=['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]):
cell = ConfigJsonCell()
cell.key = 'test-config-json-cell-2'
cell.page = page
cell.title = 'Example Site'
cell.order = 0
cell.save()
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': 'toto'}
requests_get.return_value = mock.Mock(content=json.dumps(data), status_code=200)
url = reverse('combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()})
resp = app.get(url)
assert resp.body.strip() == '/var1=toto/var2=toto/'
assert len(requests_get.mock_calls) == 2
assert requests_get.mock_calls[0][1][0] == 'http://foo'
assert requests_get.mock_calls[1][1][0] == 'http://bar'
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': 'toto'}
requests_get.return_value = mock.Mock(content=json.dumps(data), status_code=404)
url = reverse('combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()})
resp = app.get(url)
assert resp.body.strip() == '/var1=/var2=/'
assert len(requests_get.mock_calls) == 2
assert requests_get.mock_calls[0][1][0] == 'http://foo'
assert requests_get.mock_calls[1][1][0] == 'http://bar'