Commit 9fa2dc03 by Renzo Lucioni

Merge pull request #12565 from edx/renzo/program-by-id

Extend edX API utility to support retrieval of specific resources
parents 3c9a328b 62403eea
...@@ -11,23 +11,25 @@ from openedx.core.lib.token_utils import get_id_token ...@@ -11,23 +11,25 @@ from openedx.core.lib.token_utils import get_id_token
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def get_edx_api_data(api_config, user, resource, querystring=None, cache_key=None): def get_edx_api_data(api_config, user, resource, resource_id=None, querystring=None, cache_key=None):
"""Fetch data from an API using provided API configuration and resource """GET data from an edX REST API.
name.
DRY utility for handling caching and pagination.
Arguments: Arguments:
api_config (ConfigurationModel): The configuration model governing api_config (ConfigurationModel): The configuration model governing interaction with the API.
interaction with the API.
user (User): The user to authenticate as when requesting data. user (User): The user to authenticate as when requesting data.
resource(str): Name of the API resource for which data is being resource (str): Name of the API resource being requested.
requested.
querystring(dict): Querystring parameters that might be required to Keyword Arguments:
request data. resource_id (int or str): Identifies a specific resource to be retrieved.
cache_key(str): Where to cache retrieved data. Omitting this will cause the querystring (dict): Optional query string parameters.
cache to be bypassed. cache_key (str): Where to cache retrieved data. The cache will be ignored if this is omitted
(neither inspected nor updated).
Returns: Returns:
list of dict, representing data returned by the API. Data returned by the API. When hitting a list endpoint, extracts "results" (list of dict)
returned by DRF-powered APIs.
""" """
no_data = [] no_data = []
...@@ -36,34 +38,52 @@ def get_edx_api_data(api_config, user, resource, querystring=None, cache_key=Non ...@@ -36,34 +38,52 @@ def get_edx_api_data(api_config, user, resource, querystring=None, cache_key=Non
return no_data return no_data
if cache_key: if cache_key:
cache_key = '{}.{}'.format(cache_key, resource_id) if resource_id else cache_key
cached = cache.get(cache_key) cached = cache.get(cache_key)
if cached is not None: if cached:
return cached return cached
try: try:
jwt = get_id_token(user, api_config.OAUTH2_CLIENT_NAME) jwt = get_id_token(user, api_config.OAUTH2_CLIENT_NAME)
api = EdxRestApiClient(api_config.internal_api_url, jwt=jwt) api = EdxRestApiClient(api_config.internal_api_url, jwt=jwt)
except Exception: # pylint: disable=broad-except except: # pylint: disable=bare-except
log.exception('Failed to initialize the %s API client.', api_config.API_NAME) log.exception('Failed to initialize the %s API client.', api_config.API_NAME)
return no_data return no_data
try: try:
querystring = {} if not querystring else querystring endpoint = getattr(api, resource)
response = getattr(api, resource).get(**querystring) 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) results = response.get('results', no_data)
page = 1 page = 1
next_page = response.get('next', None) next_page = response.get('next')
while next_page: while next_page:
page += 1 page += 1
querystring['page'] = page querystring['page'] = page
response = getattr(api, resource).get(**querystring) response = endpoint.get(**querystring)
results += response.get('results', no_data) results += response.get('results', no_data)
next_page = response.get('next', None) next_page = response.get('next')
except Exception: # pylint: disable=broad-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 return results
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment