def test_no_response_doesnt_get_cached(self):
        """
        Response doesn't get cached when empty.
        """
        uuid = str(self.enterprise_customer.uuid)
        api_resource_name = 'enterprise-customer'
        cache_key = get_cache_key(
            resource=api_resource_name,
            querystring={},
            traverse_pagination=False,
            resource_id=uuid,
        )

        cached_enterprise_api_response = cache.get(cache_key)
        assert cached_enterprise_api_response is None

        self.mock_empty_response('enterprise-customer-courses', uuid)
        client = enterprise_api.EnterpriseApiClient(self.user)
        response = client._load_data(  # pylint: disable=protected-access
            resource=api_resource_name,
            detail_resource='courses',
            resource_id=uuid,
        )
        assert not response

        # The empty response is not cached.
        cached_api_response = cache.get(cache_key)
        assert not cached_api_response
    def test_get_content_metadata_with_enterprise_catalogs(self):
        """
        Verify that the client method `get_content_metadata` works as expected.
        """
        EnterpriseCustomerCatalogFactory(
            enterprise_customer=self.enterprise_customer,
        )
        uuid = str(self.enterprise_customer.uuid)
        course_run_ids = ['course-v1:edX+DemoX+Demo_Course_1', 'course-v1:edX+DemoX+Demo_Course_2']

        self.mock_ent_courses_api_with_pagination(
            enterprise_uuid=uuid,
            course_run_ids=course_run_ids
        )

        enterprise_catalog_uuid = str(self.enterprise_customer.enterprise_customer_catalogs.first().uuid)
        self.mock_enterprise_customer_catalogs(enterprise_catalog_uuid)

        api_resource_name = 'enterprise-customer'
        cache_key = get_cache_key(
            resource=api_resource_name,
            querystring={},
            resource_id=uuid,
            traverse_pagination=False,
        )
        cached_enterprise_api_response = cache.get(cache_key)
        self.assertIsNone(cached_enterprise_api_response)

        # Verify that by default enterprise client fetches all the course runs associated with the catalog.
        client = enterprise_api.EnterpriseApiClient(self.user)
        course_runs = client.get_content_metadata(self.enterprise_customer)
        assert len(course_runs) == 5
    def test_get_content_metadata(self):
        """
        Verify that the client method `get_content_metadata` works as expected.
        """
        uuid = str(self.enterprise_customer.uuid)
        course_run_ids = ['course-v1:edX+DemoX+Demo_Course_1', 'course-v1:edX+DemoX+Demo_Course_2']
        self.mock_ent_courses_api_with_pagination(
            enterprise_uuid=uuid,
            course_run_ids=course_run_ids
        )

        api_resource_name = 'enterprise-customer'
        cache_key = get_cache_key(
            resource=api_resource_name,
            querystring={},
            resource_id=uuid,
            traverse_pagination=False,
        )
        cached_enterprise_api_response = cache.get(cache_key)
        self.assertIsNone(cached_enterprise_api_response)

        # Verify that by default enterprise client fetches all the course runs associated with the catalog.
        client = enterprise_api.EnterpriseApiClient(self.user)
        api_response = client.get_content_metadata(self.enterprise_customer)
        self._assert_enterprise_courses_api_response(
            ['course-v1:edX+DemoX+Demo_Course_1', 'course-v1:edX+DemoX+Demo_Course_2'],
            api_response,
            expected_count=2
        )
        # Verify the enterprise API was hit twice
        self._assert_num_requests(2)
Example #4
0
    def test_no_response_doesnt_get_cached(self):
        """
        Response doesn't get cached when empty.
        """
        EnterpriseCustomerCatalogFactory(
            enterprise_customer=self.enterprise_customer, )
        enterprise_catalog_uuid = str(
            self.enterprise_customer.enterprise_customer_catalogs.first().uuid)

        api_resource_name = 'enterprise_catalogs'
        cache_key = get_cache_key(
            resource=api_resource_name,
            querystring={},
            traverse_pagination=False,
            resource_id=enterprise_catalog_uuid,
        )

        cached_enterprise_api_response = cache.get(cache_key)
        assert cached_enterprise_api_response is None

        self.mock_empty_response('enterprise-catalogs-detail',
                                 enterprise_catalog_uuid)
        client = enterprise_api.EnterpriseApiClient(self.user)
        response = client._load_data(  # pylint: disable=protected-access
            resource=api_resource_name,
            resource_id=enterprise_catalog_uuid,
        )
        assert not response

        # The empty response is not cached.
        cached_api_response = cache.get(cache_key)
        assert not cached_api_response
Example #5
0
    def test_get_content_metadata_with_enterprise_catalog_set_to_none(self):
        """
        Verify that the client method `get_content_metadata` returns courses from
        associated EnterpriseCustomerCatalog objects only if EnterpriseCustomer.catalog is set to None.
        """
        EnterpriseCustomerCatalogFactory(
            enterprise_customer=self.enterprise_customer, )

        enterprise_catalog_uuid = str(
            self.enterprise_customer.enterprise_customer_catalogs.first().uuid)
        self.mock_enterprise_customer_catalogs(enterprise_catalog_uuid)

        api_resource_name = 'enterprise-customer'
        cache_key = get_cache_key(
            resource=api_resource_name,
            querystring={},
            resource_id=str(self.enterprise_customer.uuid),
            traverse_pagination=False,
        )
        cached_enterprise_api_response = cache.get(cache_key)
        self.assertIsNone(cached_enterprise_api_response)

        # Verify that by default enterprise client fetches all the course runs associated with the enterprise catalog.
        client = enterprise_api.EnterpriseApiClient(self.user)
        course_runs = client.get_content_metadata(self.enterprise_customer)
        assert len(course_runs) == 3
    def get_course_duration(self, obj):
        """
        Get course's duration as a timedelta.

        Arguments:
            obj (CourseOverview): CourseOverview object

        Returns:
            (timedelta): Duration of a course.
        """
        course_run = None
        duration = None
        site = self.context.get('site')
        if site:
            cache_key = get_cache_key(course_id=obj.id, site=site)
            cached_response = TieredCache.get_cached_response(cache_key)
            if cached_response.is_found:
                course_run = cached_response.value
            else:
                try:
                    _, course_run = CourseCatalogApiServiceClient(site).get_course_and_course_run(str(obj.id))
                    TieredCache.set_all_tiers(cache_key, course_run, CACHE_TIMEOUT)
                except ImproperlyConfigured:
                    LOGGER.warning('CourseCatalogApiServiceClient is improperly configured.')
        if course_run and course_run.get('max_effort') and course_run.get('weeks_to_complete'):
            duration = '{effort} hours per week for {weeks} weeks.'.format(
                effort=course_run['max_effort'],
                weeks=course_run['weeks_to_complete']
            )
        return duration or ''
Example #7
0
    def get_catalog_results(self,
                            content_filter_query,
                            query_params=None,
                            traverse_pagination=False):
        """
            Return results from the cache or discovery service's search/all endpoint.
        Arguments:
            content_filter_query (dict): query parameters used to filter catalog results.
            query_params (dict): query parameters used to paginate results.
            traverse_pagination (bool): True to return all results, False to return the paginated response.
                                        Defaults to False.

        Returns:
            dict: Paginated response or all the records.
        """
        query_params = query_params or {}

        try:
            cache_key = utils.get_cache_key(
                service='discovery',
                endpoint=self.SEARCH_ALL_ENDPOINT,
                query=content_filter_query,
                traverse_pagination=traverse_pagination,
                **query_params)
            response = cache.get(cache_key)
            if not response:
                LOGGER.info(
                    'ENT-2390-1 | Calling discovery service for search/all/ '
                    'data with content_filter_query %s and query_params %s',
                    content_filter_query,
                    query_params,
                )
                # Response is not cached, so make a call.
                response = self.get_catalog_results_from_discovery(
                    content_filter_query, query_params, traverse_pagination)
                response_as_string = pickle.dumps(response)
                LOGGER.info(
                    'ENT-2489 | Response from content_filter_query %s is %d bytes long.',
                    content_filter_query, len(response_as_string))
                cache.set(cache_key, response,
                          settings.ENTERPRISE_API_CACHE_TIMEOUT)
            else:
                LOGGER.info(
                    'ENT-2390-2 | Got search/all/ data from the cache with '
                    'content_filter_query %s and query_params %s',
                    content_filter_query,
                    query_params,
                )
        except Exception as ex:  # pylint: disable=broad-except
            LOGGER.exception(
                'Attempted to call course-discovery search/all/ endpoint with the following parameters: '
                'content_filter_query: %s, query_params: %s, traverse_pagination: %s. '
                'Failed to retrieve data from the catalog API. content -- [%s]',
                content_filter_query, query_params, traverse_pagination,
                getattr(ex, 'content', ''))
            # We need to bubble up failures when we encounter them instead of masking them!
            raise ex

        return response
 def test_skip_request_if_response_cached(self):
     """
     We skip the request portion of the API's logic if the response is already cached.
     """
     cache_key = get_cache_key(
         resource='resource',
         querystring={},
         traverse_pagination=False,
         resource_id=None,
     )
     cache_value = {'fake': 'response'}
     cache.set(cache_key, cache_value, settings.ENTERPRISE_API_CACHE_TIMEOUT)
     client = enterprise_api.EnterpriseApiClient(self.user)
     response = client._load_data('resource')  # pylint: disable=protected-access
     assert response == cache_value
Example #9
0
    def _load_data(
        self,
        resource,
        detail_resource=None,
        resource_id=None,
        querystring=None,
        traverse_pagination=False,
        default=DEFAULT_VALUE_SAFEGUARD,
    ):
        """
        Loads a response from a call to one of the Enterprise endpoints.

        :param resource: The endpoint resource name.
        :param detail_resource: The sub-resource to append to the path.
        :param resource_id: The resource ID for the specific detail to get from the endpoint.
        :param querystring: Optional query string parameters.
        :param traverse_pagination: Whether to traverse pagination or return paginated response.
        :param default: The default value to return in case of no response content.
        :return: Data returned by the API.
        """
        default_val = default if default != self.DEFAULT_VALUE_SAFEGUARD else {}
        querystring = querystring if querystring else {}

        cache_key = utils.get_cache_key(
            resource=resource,
            querystring=querystring,
            traverse_pagination=traverse_pagination,
            resource_id=resource_id)
        response = cache.get(cache_key)
        if not response:
            # Response is not cached, so make a call.
            endpoint = getattr(self.client, resource)(resource_id)
            endpoint = getattr(
                endpoint, detail_resource) if detail_resource else endpoint
            response = endpoint.get(**querystring)
            if traverse_pagination:
                results = utils.traverse_pagination(response, endpoint)
                response = {
                    'count': len(results),
                    'next': 'None',
                    'previous': 'None',
                    'results': results,
                }
            if response:
                # Now that we've got a response, cache it.
                cache.set(cache_key, response,
                          settings.ENTERPRISE_API_CACHE_TIMEOUT)
        return response or default_val