def get_course_uuid_for_course(course_run_key): """ Retrieve the Course UUID for a given course key Arguments: course_run_key (CourseKey): A Key for a Course run that will be pulled apart to get just the information required for a Course (e.g. org+course) Returns: UUID: Course UUID and None if it was not retrieved. """ user, catalog_integration = check_catalog_integration_and_get_user( error_message_field='Course UUID') if user: api_client = get_catalog_api_client(user) base_api_url = get_catalog_api_base_url() run_cache_key = f"{catalog_integration.CACHE_KEY}.course_run.{course_run_key}" course_run_data = get_api_data( catalog_integration, 'course_runs', resource_id=str(course_run_key), api_client=api_client, base_api_url=base_api_url, cache_key=run_cache_key if catalog_integration.is_cache_enabled else None, long_term_cache=True, many=False, ) course_key_str = course_run_data.get('course', None) if course_key_str: run_cache_key = f"{catalog_integration.CACHE_KEY}.course.{course_key_str}" data = get_api_data( catalog_integration, 'courses', resource_id=course_key_str, api_client=api_client, base_api_url=base_api_url, cache_key=run_cache_key if catalog_integration.is_cache_enabled else None, long_term_cache=True, many=False, ) uuid_str = data.get('uuid', None) if uuid_str: return uuid.UUID(uuid_str) return None
def get_program_types(name=None): """Retrieve program types from the catalog service. Keyword Arguments: name (string): Name identifying a specific program type. Returns: list of dict, representing program types. dict, if a specific program type is requested. """ user, catalog_integration = check_catalog_integration_and_get_user( error_message_field='Program types') if user: cache_key = f'{catalog_integration.CACHE_KEY}.program_types' data = get_api_data(catalog_integration, "program_types", api_client=get_catalog_api_client(user), base_api_url=get_catalog_api_base_url(), cache_key=cache_key if catalog_integration.is_cache_enabled else None) # Filter by name if a name was provided if name: data = next(program_type for program_type in data if program_type['name'] == name) return data else: return []
def test_get_specific_resource(self): """ Verify that a specific resource can be retrieved. """ catalog_integration = self.create_catalog_integration() api = get_catalog_api_client(self.user) resource_id = 1 url = urljoin(f"{self.base_api_url}/", f"programs/{resource_id}/") expected_resource = {'key': 'value'} self._mock_catalog_api([ httpretty.Response(body=json.dumps(expected_resource), content_type='application/json') ], url=url) actual_resource = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url, resource_id=resource_id) assert actual_resource == expected_resource self._assert_num_requests(1)
def test_get_paginated_data_do_not_traverse_pagination(self): """ Verify that pagination is not traversed if traverse_pagination=False is passed as argument. """ catalog_integration = self.create_catalog_integration() api = get_catalog_api_client(self.user) url = urljoin(f"{self.base_api_url}/", "/programs/?page={}") responses = [ { 'next': url.format(2), 'results': ['some'], }, { 'next': url.format(None), 'results': ['test'], }, ] expected_response = responses[0] self._mock_catalog_api([ httpretty.Response(body=json.dumps(body), content_type='application/json') for body in responses ]) actual_collection = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url, traverse_pagination=False) assert actual_collection == expected_response self._assert_num_requests(1)
def get_course_run_details(course_run_key, fields): """ Retrieve information about the course run with the given id Arguments: course_run_key: key for the course_run about which we are retrieving information Returns: dict with language, start date, end date, and max_effort details about specified course run """ course_run_details = {} user, catalog_integration = check_catalog_integration_and_get_user( error_message_field=f'Data for course_run {course_run_key}') if user: cache_key = f'{catalog_integration.CACHE_KEY}.course_runs' course_run_details = get_api_data( catalog_integration, 'course_runs', api_client=get_catalog_api_client(user), base_api_url=get_catalog_api_base_url(), resource_id=course_run_key, cache_key=cache_key, many=False, traverse_pagination=False, fields=fields) return course_run_details
def test_get_unpaginated_data(self): """ Verify that unpaginated data can be retrieved. """ catalog_integration = self.create_catalog_integration() api = get_catalog_api_client(self.user) expected_collection = ['some', 'test', 'data'] data = { 'next': None, 'results': expected_collection, } self._mock_catalog_api([ httpretty.Response(body=json.dumps(data), content_type='application/json') ]) with mock.patch('requests.Session') as mock_init: actual_collection = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url) # Verify that the helper function didn't initialize its own client. assert not mock_init.called assert actual_collection == expected_collection # Verify the API was actually hit (not the cache) self._assert_num_requests(1)
def test_get_paginated_data(self): """ Verify that paginated data can be retrieved. """ catalog_integration = self.create_catalog_integration() api = get_catalog_api_client(self.user) expected_collection = ['some', 'test', 'data'] url = urljoin(f"{self.base_api_url}/", "/programs/?page={}") responses = [] for page, record in enumerate(expected_collection, start=1): data = { 'next': url.format(page + 1) if page < len(expected_collection) else None, 'results': [record], } body = json.dumps(data) responses.append( httpretty.Response(body=body, content_type='application/json')) self._mock_catalog_api(responses) actual_collection = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url) assert actual_collection == expected_collection self._assert_num_requests(len(expected_collection))
def test_get_specific_resource_with_falsey_id(self): """ Check the call with falsey resource_id. Verify that a specific resource can be retrieved, and pagination parsing is not attempted, if the ID passed is falsey (e.g., 0). The expected resource contains a "results" key, as a paginatable item would have, so if the function looks for falsey values in the resource_id field, rather than specifically None, the function will return the value of that "results" key. """ catalog_integration = self.create_catalog_integration() api = get_catalog_api_client(self.user) resource_id = 0 url = urljoin(f"{self.base_api_url}/", f"programs/{resource_id}/") expected_resource = {'key': 'value', 'results': []} self._mock_catalog_api([ httpretty.Response(body=json.dumps(expected_resource), content_type='application/json') ], url=url) actual_resource = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url, resource_id=resource_id) assert actual_resource == expected_resource self._assert_num_requests(1)
def test_get_specific_fields_from_cache_response(self): """ Verify that resource response is cached and get required fields from cached response. """ catalog_integration = self.create_catalog_integration(cache_ttl=5) api = get_catalog_api_client(self.user) response = {'lang': 'en', 'weeks_to_complete': '5'} resource_id = 'course-v1:testX+testABC+1T2019' url = urljoin(f"{self.base_api_url}/", f"course_runs/{resource_id}/") expected_resource_for_lang = {'lang': 'en'} expected_resource_for_weeks_to_complete = {'weeks_to_complete': '5'} self._mock_catalog_api([ httpretty.Response(body=json.dumps(response), content_type='application/json') ], url=url) cache_key = CatalogIntegration.current().CACHE_KEY # get response and set the cache. actual_resource_for_lang = get_api_data(catalog_integration, 'course_runs', resource_id=resource_id, api_client=api, base_api_url=self.base_api_url, cache_key=cache_key, fields=['lang']) assert actual_resource_for_lang == expected_resource_for_lang # Hit the cache actual_resource = get_api_data(catalog_integration, 'course_runs', api_client=api, base_api_url=self.base_api_url, resource_id=resource_id, cache_key=cache_key, fields=['weeks_to_complete']) assert actual_resource == expected_resource_for_weeks_to_complete # Verify that only one requests were made, not three. self._assert_num_requests(1)
def test_api_config_disabled(self, mock_warning): """ Verify that no data is retrieved if the provided config model is disabled. """ catalog_integration = self.create_catalog_integration(enabled=False) actual = get_api_data(catalog_integration, 'programs', api_client=None, base_api_url=self.base_api_url) assert mock_warning.called assert actual == []
def test_api_config_disabled_with_id_and_not_collection( self, mock_warning): """ Verify that no data is retrieved if the provided config model is disabled. """ catalog_integration = self.create_catalog_integration(enabled=False) actual = get_api_data(catalog_integration, 'programs', api_client=None, base_api_url=self.base_api_url, resource_id=100, many=False) assert mock_warning.called assert actual == {}
def get_credentials(user, program_uuid=None, credential_type=None): """ Given a user, get credentials earned from the credentials service. Arguments: user (User): The user to authenticate as when requesting credentials. Keyword Arguments: program_uuid (str): UUID of the program whose credential to retrieve. credential_type (str): Which type of credentials to return (course-run or program) Returns: list of dict, representing credentials returned by the Credentials service. """ credential_configuration = CredentialsApiConfig.current() querystring = { 'username': user.username, 'status': 'awarded', 'only_visible': 'True' } if program_uuid: querystring['program_uuid'] = program_uuid if credential_type: querystring['type'] = credential_type # Bypass caching for staff users, who may be generating credentials and # want to see them displayed immediately. use_cache = credential_configuration.is_cache_enabled and not user.is_staff cache_key = f'{credential_configuration.CACHE_KEY}.{user.username}' if use_cache else None if cache_key and program_uuid: cache_key = f'{cache_key}.{program_uuid}' api_client = get_credentials_api_client(user) base_api_url = get_credentials_api_base_url() return get_api_data(credential_configuration, 'credentials', api_client=api_client, base_api_url=base_api_url, querystring=querystring, cache_key=cache_key)
def get_owners_for_course(course_uuid): # lint-amnesty, pylint: disable=missing-function-docstring user, catalog_integration = check_catalog_integration_and_get_user( error_message_field='Owners') if user: cache_key = f"{catalog_integration.CACHE_KEY}.course.{course_uuid}.course_runs" data = get_api_data(catalog_integration, 'courses', resource_id=course_uuid, api_client=get_catalog_api_client(user), base_api_url=get_catalog_api_base_url(), cache_key=cache_key if catalog_integration.is_cache_enabled else None, long_term_cache=True, many=False) return data.get('owners', []) return []
def test_data_retrieval_failure(self, mock_exception): """ Verify that an exception is logged when data can't be retrieved. """ catalog_integration = self.create_catalog_integration() api = get_catalog_api_client(self.user) self._mock_catalog_api([ httpretty.Response(body='clunk', content_type='application/json', status_code=500) ]) actual = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url) assert mock_exception.called assert actual == []
def get_currency_data(): """Retrieve currency data from the catalog service. Returns: list of dict, representing program types. dict, if a specific program type is requested. """ user, catalog_integration = check_catalog_integration_and_get_user( error_message_field='Currency data') if user: cache_key = f'{catalog_integration.CACHE_KEY}.currency' return get_api_data(catalog_integration, "currency", api_client=get_catalog_api_client(user), base_api_url=get_catalog_api_base_url(), traverse_pagination=False, cache_key=cache_key if catalog_integration.is_cache_enabled else None) else: return []
def get_course_runs(): """ Retrieve all the course runs from the catalog service. Returns: list of dict with each record representing a course run. """ course_runs = [] user, catalog_integration = check_catalog_integration_and_get_user( error_message_field='Course runs') if user: querystring = { 'page_size': catalog_integration.page_size, 'exclude_utm': 1, } course_runs = get_api_data(catalog_integration, 'course_runs', api_client=get_catalog_api_client(user), base_api_url=get_catalog_api_base_url(), querystring=querystring) return course_runs
def get_user_orders(user): """Given a user, get the detail of all the orders from the Ecommerce service. Args: user (User): The user to authenticate as when requesting ecommerce. Returns: list of dict, representing orders returned by the Ecommerce service. """ user_orders = [] commerce_configuration = CommerceConfiguration.current() user_query = {'username': user.username} use_cache = commerce_configuration.is_cache_enabled cache_key = commerce_configuration.CACHE_KEY + '.' + str(user.id) if use_cache else None commerce_user_orders = get_api_data( commerce_configuration, 'orders', api_client=get_ecommerce_api_client(user), base_api_url=get_ecommerce_api_base_url(), querystring=user_query, cache_key=cache_key ) for order in commerce_user_orders: if order['status'].lower() == 'complete': date_placed = datetime.strptime(order['date_placed'], "%Y-%m-%dT%H:%M:%SZ") order_data = { 'number': order['number'], 'price': order['total_excl_tax'], 'order_date': strftime_localized(date_placed, 'SHORT_DATE'), 'receipt_url': EcommerceService().get_receipt_page_url(order['number']), 'lines': order['lines'], } user_orders.append(order_data) return user_orders
def test_cache_utilization(self): """ Verify that when enabled, the cache is used. """ catalog_integration = self.create_catalog_integration(cache_ttl=5) api = get_catalog_api_client(self.user) expected_collection = ['some', 'test', 'data'] data = { 'next': None, 'results': expected_collection, } self._mock_catalog_api([ httpretty.Response(body=json.dumps(data), content_type='application/json') ], ) resource_id = 1 url = urljoin(f"{self.base_api_url}/", f"programs/{resource_id}/") expected_resource = {'key': 'value'} self._mock_catalog_api([ httpretty.Response(body=json.dumps(expected_resource), content_type='application/json') ], url=url) cache_key = CatalogIntegration.current().CACHE_KEY # Warm up the cache. get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url, cache_key=cache_key) get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url, resource_id=resource_id, cache_key=cache_key) # Hit the cache. actual_collection = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url, cache_key=cache_key) assert actual_collection == expected_collection actual_resource = get_api_data(catalog_integration, 'programs', api_client=api, base_api_url=self.base_api_url, resource_id=resource_id, cache_key=cache_key) assert actual_resource == expected_resource # Verify that only two requests were made, not four. self._assert_num_requests(2)