From abaece02ce1c37b9f8675b1afab890c92c76a27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Mon, 26 Jun 2017 10:16:37 +0200 Subject: [PATCH] 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]" } ] } --- combo/data/models.py | 69 +++++++++++++++++++++++++++++++++++++--------------- tests/test_cells.py | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 20 deletions(-) diff --git a/combo/data/models.py b/combo/data/models.py index 3311e4d..2f2b603 100644 --- a/combo/data/models.py +++ b/combo/data/models.py @@ -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 @@ -1008,6 +1033,10 @@ class ConfigJsonCell(JsonCellBase): 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 diff --git a/tests/test_cells.py b/tests/test_cells.py index 393e8ee..112b9f0 100644 --- a/tests/test_cells.py +++ b/tests/test_cells.py @@ -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' -- 2.13.3