"""Helper functions to get data from APIs""" from __future__ import unicode_literals import logging from django.core.cache import cache from edx_rest_api_client.client import EdxRestApiClient from openedx.core.lib.token_utils import get_id_token log = logging.getLogger(__name__) def get_edx_api_data(api_config, user, resource, resource_id=None, querystring=None, cache_key=None): """GET data from an edX REST API. DRY utility for handling caching and pagination. Arguments: api_config (ConfigurationModel): The configuration model governing interaction with the API. user (User): The user to authenticate as when requesting data. resource (str): Name of the API resource being requested. Keyword Arguments: resource_id (int or str): Identifies a specific resource to be retrieved. querystring (dict): Optional query string parameters. cache_key (str): Where to cache retrieved data. The cache will be ignored if this is omitted (neither inspected nor updated). Returns: Data returned by the API. When hitting a list endpoint, extracts "results" (list of dict) returned by DRF-powered APIs. """ no_data = [] if not api_config.enabled: log.warning('%s configuration is disabled.', api_config.API_NAME) return no_data if cache_key: cache_key = '{}.{}'.format(cache_key, resource_id) if resource_id else cache_key cached = cache.get(cache_key) if cached: return cached try: jwt = get_id_token(user, api_config.OAUTH2_CLIENT_NAME) api = EdxRestApiClient(api_config.internal_api_url, jwt=jwt) except: # pylint: disable=bare-except log.exception('Failed to initialize the %s API client.', api_config.API_NAME) return no_data try: endpoint = getattr(api, resource) querystring = querystring if querystring else {} response = endpoint(resource_id).get(**querystring) if resource_id: results = response else: results = _traverse_pagination(response, endpoint, querystring, no_data) except: # pylint: disable=bare-except log.exception('Failed to retrieve data from the %s API.', api_config.API_NAME) return no_data if cache_key: cache.set(cache_key, results, api_config.cache_ttl) return results def _traverse_pagination(response, endpoint, querystring, no_data): """Traverse a paginated API response. Extracts and concatenates "results" (list of dict) returned by DRF-powered APIs. """ results = response.get('results', no_data) page = 1 next_page = response.get('next') while next_page: page += 1 querystring['page'] = page response = endpoint.get(**querystring) results += response.get('results', no_data) next_page = response.get('next') return results