def test_api_client_not_provided(self):
        """Verify that an API client doesn't need to be provided."""
        ClientFactory(name=CredentialsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)

        credentials_api_config = self.create_credentials_config()

        with mock.patch('openedx.core.lib.edx_api_utils.EdxRestApiClient.__init__') as mock_init:
            get_edx_api_data(credentials_api_config, self.user, 'credentials')
            self.assertTrue(mock_init.called)
 def test_client_passed(self):
     """ Verify that when API client is passed edx_rest_api_client is not
     used.
     """
     program_config = self.create_programs_config()
     api = ecommerce_api_client(self.user)
     with mock.patch('openedx.core.lib.edx_api_utils.EdxRestApiClient.__init__') as mock_init:
         get_edx_api_data(program_config, self.user, 'orders', api=api)
         self.assertFalse(mock_init.called)
    def test_get_edx_api_data_cache(self):
        """Verify that when enabled, the cache is used."""
        program_config = self.create_programs_config(cache_ttl=1)
        self.mock_programs_api()

        # Warm up the cache.
        get_edx_api_data(program_config, self.user, 'programs', cache_key='test.key')

        # Hit the cache.
        get_edx_api_data(program_config, self.user, 'programs', cache_key='test.key')

        # Verify only one request was made.
        self.assertEqual(len(httpretty.httpretty.latest_requests), 1)
Example #4
0
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 = create_catalog_api_client(user)

        run_cache_key = '{base}.course_run.{course_run_key}'.format(
            base=catalog_integration.CACHE_KEY,
            course_run_key=course_run_key
        )

        course_run_data = get_edx_api_data(
            catalog_integration,
            'course_runs',
            resource_id=unicode(course_run_key),
            api=api,
            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 = '{base}.course.{course_key}'.format(
                base=catalog_integration.CACHE_KEY,
                course_key=course_key_str
            )

            data = get_edx_api_data(
                catalog_integration,
                'courses',
                resource_id=course_key_str,
                api=api,
                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
Example #5
0
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 = create_catalog_api_client(user)

        run_cache_key = '{base}.course_run.{course_run_key}'.format(
            base=catalog_integration.CACHE_KEY, course_run_key=course_run_key)

        course_run_data = get_edx_api_data(
            catalog_integration,
            'course_runs',
            resource_id=text_type(course_run_key),
            api=api,
            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 = '{base}.course.{course_key}'.format(
                base=catalog_integration.CACHE_KEY, course_key=course_key_str)

            data = get_edx_api_data(
                catalog_integration,
                'courses',
                resource_id=course_key_str,
                api=api,
                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
Example #6
0
def get_program_types(name=None):
    """Retrieve program types from the catalog service.

    Keyword Arguments:
        name (string): Name identifying a specific program.

    Returns:
        list of dict, representing program types.
        dict, if a specific program type is requested.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            return []

        api = create_catalog_api_client(user)
        cache_key = '{base}.program_types'.format(
            base=catalog_integration.CACHE_KEY)

        data = get_edx_api_data(catalog_integration,
                                'program_types',
                                api=api,
                                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_paginated_data(self):
        """Verify that paginated data can be retrieved."""
        program_config = self.create_programs_config()

        expected_collection = ['some', 'test', 'data']
        url = ProgramsApiConfig.current().internal_api_url.strip('/') + '/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_programs_api(responses)

        actual_collection = get_edx_api_data(program_config, self.user, 'programs')
        self.assertEqual(actual_collection, expected_collection)

        self._assert_num_requests(len(expected_collection))
Example #8
0
    def _load_data(self, resource, default=DEFAULT_VALUE_SAFEGUARD, **kwargs):
        """
        Load data from API client.

        Arguments:
            resource(string): type of resource to load
            default(any): value to return if API query returned empty result. Sensible values: [], {}, None etc.

        Returns:
            dict: Deserialized response from Course Catalog API

        """
        default_val = default if default != self.DEFAULT_VALUE_SAFEGUARD else {}
        try:
            return get_edx_api_data(
                api_config=CatalogIntegration.current(),
                resource=resource,
                api=self.client,
                **kwargs
            ) or default_val
        except (SlumberBaseException, ConnectionError, Timeout) as exc:
            LOGGER.exception(
                'Failed to load data from resource [%s] with kwargs [%s] due to: [%s]',
                resource, kwargs, str(exc)
            )
            return default_val
Example #9
0
def get_course_run(course_key, user):
    """Get a course run's data from the course catalog service.

    Arguments:
        course_key (CourseKey): Course key object identifying the run whose data we want.
        user (User): The user to authenticate as when making requests to the catalog service.

    Returns:
        dict, empty if no data could be retrieved.
    """
    catalog_integration = CatalogIntegration.current()

    if catalog_integration.enabled:
        api = create_catalog_api_client(user, catalog_integration)

        data = get_edx_api_data(
            catalog_integration,
            user,
            'course_runs',
            resource_id=unicode(course_key),
            cache_key=catalog_integration.CACHE_KEY if catalog_integration.is_cache_enabled else None,
            api=api,
        )

        return data if data else {}
    else:
        return {}
Example #10
0
def get_program_types():
    """Retrieve all program types from the catalog service.

    Returns:
        list of dict, representing program types.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        try:
            user = User.objects.get(username=catalog_integration.service_username)
        except User.DoesNotExist:
            return []

        api = create_catalog_api_client(user, catalog_integration)
        cache_key = '{base}.program_types'.format(base=catalog_integration.CACHE_KEY)

        return get_edx_api_data(
            catalog_integration,
            user,
            'program_types',
            cache_key=cache_key if catalog_integration.is_cache_enabled else None,
            api=api
        )
    else:
        return []
Example #11
0
def get_credentials(user, program_uuid=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.

    Returns:
        list of dict, representing credentials returned by the Credentials
        service.
    """
    credential_configuration = CredentialsApiConfig.current()

    querystring = {'username': user.username, 'status': 'awarded'}

    if program_uuid:
        querystring['program_uuid'] = program_uuid

    # 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 = credential_configuration.CACHE_KEY + '.' + user.username if use_cache else None

    return get_edx_api_data(
        credential_configuration, user, 'credentials', querystring=querystring, cache_key=cache_key
    )
Example #12
0
def get_programs(user, program_id=None):
    """Given a user, get programs from the Programs service.
    Returned value is cached depending on user permissions. Staff users making requests
    against Programs will receive unpublished programs, while regular users will only receive
    published programs.

    Arguments:
        user (User): The user to authenticate as when requesting programs.

    Keyword Arguments:
        program_id (int): Identifies a specific program for which to retrieve data.

    Returns:
        list of dict, representing programs returned by the Programs service.
    """
    programs_config = ProgramsApiConfig.current()

    # Bypass caching for staff users, who may be creating Programs and want
    # to see them displayed immediately.
    cache_key = programs_config.CACHE_KEY if programs_config.is_cache_enabled and not user.is_staff else None
    return get_edx_api_data(programs_config,
                            user,
                            'programs',
                            resource_id=program_id,
                            cache_key=cache_key)
Example #13
0
def get_course_run(course_key, user):
    """Get a course run's data from the course catalog service.

    Arguments:
        course_key (CourseKey): Course key object identifying the run whose data we want.
        user (User): The user to authenticate as when making requests to the catalog service.

    Returns:
        dict, empty if no data could be retrieved.
    """
    catalog_integration = CatalogIntegration.current()

    if catalog_integration.enabled:
        api = create_catalog_api_client(user, catalog_integration)

        data = get_edx_api_data(
            catalog_integration,
            user,
            'course_runs',
            resource_id=unicode(course_key),
            cache_key=catalog_integration.CACHE_KEY
            if catalog_integration.is_cache_enabled else None,
            api=api,
            querystring={'exclude_utm': 1},
        )

        return data if data else {}
    else:
        return {}
    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 = create_catalog_api_client(self.user)

        url = CatalogIntegration.current().get_internal_api_url().strip('/') + '/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_edx_api_data(catalog_integration, 'programs', api=api, traverse_pagination=False)
        self.assertEqual(actual_collection, expected_response)
        self._assert_num_requests(1)
Example #15
0
def get_course_runs_for_course(course_uuid):
    catalog_integration = CatalogIntegration.current()

    if catalog_integration.is_enabled():
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            logger.error(
                'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
                catalog_integration.service_username,
            )
            return []

        api = create_catalog_api_client(user)
        cache_key = '{base}.course.{uuid}.course_runs'.format(
            base=catalog_integration.CACHE_KEY,
            uuid=course_uuid
        )
        data = get_edx_api_data(
            catalog_integration,
            'courses',
            resource_id=course_uuid,
            api=api,
            cache_key=cache_key if catalog_integration.is_cache_enabled else None,
            long_term_cache=True
        )

        return data.get('course_runs', [])
    else:
        return []
Example #16
0
def get_programs(user, program_id=None):
    """Given a user, get programs from the Programs service.

    Returned value is cached depending on user permissions. Staff users making requests
    against Programs will receive unpublished programs, while regular users will only receive
    published programs.

    Arguments:
        user (User): The user to authenticate as when requesting programs.

    Keyword Arguments:
        program_id (int): Identifies a specific program for which to retrieve data.

    Returns:
        list of dict, representing programs returned by the Programs service.
        dict, if a specific program is requested.
    """
    programs_config = ProgramsApiConfig.current()

    # Bypass caching for staff users, who may be creating Programs and want
    # to see them displayed immediately.
    cache_key = programs_config.CACHE_KEY if programs_config.is_cache_enabled and not user.is_staff else None

    programs = get_edx_api_data(programs_config, user, 'programs', resource_id=program_id, cache_key=cache_key)

    # Mix in munged MicroMasters data from the catalog.
    if not program_id:
        programs += [
            munge_catalog_program(micromaster) for micromaster in get_catalog_programs(user, type='MicroMasters')
        ]

    return programs
Example #17
0
def get_program_types(name=None):
    """Retrieve program types from the catalog service.

    Keyword Arguments:
        name (string): Name identifying a specific program.

    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:
        api = create_catalog_api_client(user)
        cache_key = '{base}.program_types'.format(base=catalog_integration.CACHE_KEY)

        data = get_edx_api_data(catalog_integration, 'program_types', api=api,
                                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 []
Example #18
0
    def test_get_paginated_data(self):
        """Verify that paginated data can be retrieved."""
        catalog_integration = self.create_catalog_integration()
        api = create_catalog_api_client(self.user)

        expected_collection = ['some', 'test', 'data']
        url = CatalogIntegration.current().get_internal_api_url().strip(
            '/') + '/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_edx_api_data(catalog_integration,
                                             'programs',
                                             api=api)
        assert actual_collection == expected_collection

        self._assert_num_requests(len(expected_collection))
Example #19
0
def get_programs(uuid=None, types=None):  # pylint: disable=redefined-builtin
    """Retrieve marketable programs from the catalog service.

    Keyword Arguments:
        uuid (string): UUID identifying a specific program.
        types (list of string): List of program type names used to filter programs by type
                                (e.g., ["MicroMasters"] will only return MicroMasters programs,
                                ["MicroMasters", "XSeries"] will return MicroMasters and XSeries programs).

    Returns:
        list of dict, representing programs.
        dict, if a specific program is requested.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        try:
            user = User.objects.get(
                username=catalog_integration.service_username)
        except User.DoesNotExist:
            return []

        api = create_catalog_api_client(user, catalog_integration)
        types_param = ','.join(types) if types else None

        cache_key = '{base}.programs{types}'.format(
            base=catalog_integration.CACHE_KEY,
            types='.' + types_param if types_param else '')

        querystring = {
            'exclude_utm': 1,
        }

        # TODO ECOM-7650: Remove this after https://github.com/edx/course-discovery/pull/805 is merged and released.
        if waffle.switch_is_active(
                'display_retired_programs_on_learner_dashboard'):
            querystring['status'] = (
                'active',
                'retired',
            )
        else:
            querystring['marketable'] = 1

        if uuid:
            querystring['use_full_course_serializer'] = 1

        if types_param:
            querystring['types'] = types_param

        return get_edx_api_data(
            catalog_integration,
            user,
            'programs',
            resource_id=uuid,
            cache_key=cache_key
            if catalog_integration.is_cache_enabled else None,
            api=api,
            querystring=querystring,
        )
    else:
        return []
Example #20
0
def get_credentials(user, program_uuid=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.

    Returns:
        list of dict, representing credentials returned by the Credentials
        service.
    """
    credential_configuration = CredentialsApiConfig.current()

    querystring = {'username': user.username, 'status': 'awarded'}

    if program_uuid:
        querystring['program_uuid'] = program_uuid

    # 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 = credential_configuration.CACHE_KEY + '.' + user.username if use_cache else None
    api = get_credentials_api_client(user)

    return get_edx_api_data(
        credential_configuration, 'credentials', api=api, querystring=querystring, cache_key=cache_key
    )
Example #21
0
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
    """
    catalog_integration = CatalogIntegration.current()
    course_run_details = dict()
    if catalog_integration.enabled:
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            msg = 'Catalog service user {} does not exist. Data for course_run {} will not be retrieved'.format(
                catalog_integration.service_username,
                course_run_key
            )
            logger.error(msg)
            return course_run_details
        api = create_catalog_api_client(user)

        cache_key = '{base}.course_runs'.format(base=catalog_integration.CACHE_KEY)

        course_run_details = get_edx_api_data(catalog_integration, 'course_runs', api, resource_id=course_run_key,
                                              cache_key=cache_key, many=False, traverse_pagination=False, fields=fields)
    else:
        msg = 'Unable to retrieve details about course_run {} because Catalog Integration is not enabled'.format(
            course_run_key
        )
        logger.error(msg)
    return course_run_details
    def test_get_specific_resource_with_falsey_id(self):
        """
        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 = create_catalog_api_client(self.user)

        resource_id = 0
        url = '{api_root}/programs/{resource_id}/'.format(
            api_root=CatalogIntegration.current().get_internal_api_url().strip('/'),
            resource_id=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_edx_api_data(catalog_integration, 'programs', api=api, resource_id=resource_id)
        self.assertEqual(actual_resource, expected_resource)

        self._assert_num_requests(1)
Example #23
0
def get_course_run(course_key, user):
    """Get a course run's data from the course catalog service.

    Arguments:
        course_key (CourseKey): Course key object identifying the run whose data we want.
        user (User): The user to authenticate as when making requests to the catalog service.

    Returns:
        dict, empty if no data could be retrieved.
    """
    catalog_integration = CatalogIntegration.current()

    if catalog_integration.enabled:
        scopes = ['email', 'profile']
        expires_in = settings.OAUTH_ID_TOKEN_EXPIRATION
        jwt = JwtBuilder(user).build_token(scopes, expires_in)
        api = EdxRestApiClient(catalog_integration.internal_api_url, jwt=jwt)

        data = get_edx_api_data(
            catalog_integration,
            user,
            'course_runs',
            resource_id=unicode(course_key),
            cache_key=catalog_integration.CACHE_KEY
            if catalog_integration.is_cache_enabled else None,
            api=api,
        )

        return data if data else {}
    else:
        return {}
Example #24
0
    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 = create_catalog_api_client(self.user)

        url = CatalogIntegration.current().get_internal_api_url().strip(
            '/') + '/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_edx_api_data(catalog_integration,
                                             'programs',
                                             api=api,
                                             traverse_pagination=False)
        assert actual_collection == expected_response
        self._assert_num_requests(1)
Example #25
0
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
    api = ecommerce_api_client(user)
    commerce_user_orders = get_edx_api_data(
        commerce_configuration, 'orders', api=api, querystring=user_query, cache_key=cache_key
    )
    log.info('----------------------------------------Orders------------------------------------%s', commerce_user_orders)
    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
Example #26
0
def get_program_types(name=None):
    """Retrieve program types from the catalog service.

    Keyword Arguments:
        name (string): Name identifying a specific program.

    Returns:
        list of dict, representing program types.
        dict, if a specific program type is requested.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            return []

        api = create_catalog_api_client(user)
        cache_key = '{base}.program_types'.format(base=catalog_integration.CACHE_KEY)

        data = get_edx_api_data(catalog_integration, 'program_types', api=api,
                                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 []
Example #27
0
def get_programs(user, program_id=None):
    """Given a user, get programs from the Programs service.
    Returned value is cached depending on user permissions. Staff users making requests
    against Programs will receive unpublished programs, while regular users will only receive
    published programs.

    Arguments:
        user (User): The user to authenticate as when requesting programs.

    Keyword Arguments:
        program_id (int): Identifies a specific program for which to retrieve data.

    Returns:
        list of dict, representing programs returned by the Programs service.
    """
    programs_config = ProgramsApiConfig.current()

    # Bypass caching for staff users, who may be creating Programs and want
    # to see them displayed immediately.
    cache_key = programs_config.CACHE_KEY if programs_config.is_cache_enabled and not user.is_staff else None

    data = get_edx_api_data(programs_config, user, 'programs', resource_id=program_id, cache_key=cache_key)

    # TODO: Temporary, to be removed once category names are cased for display. ECOM-5018.
    if data and program_id:
        data['category'] = data['category'].lower()
    else:
        for program in data:
            program['category'] = program['category'].lower()

    return data
Example #28
0
def get_course_runs():
    """
    Retrieve all the course runs from the catalog service.

    Returns:
        list of dict with each record representing a course run.
    """
    catalog_integration = CatalogIntegration.current()
    course_runs = []
    if catalog_integration.enabled:
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            logger.error(
                'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
                catalog_integration.service_username,
            )
            return course_runs

        api = create_catalog_api_client(user)

        querystring = {
            'page_size': catalog_integration.page_size,
            'exclude_utm': 1,
        }

        course_runs = get_edx_api_data(catalog_integration, 'course_runs', api=api, querystring=querystring)

    return course_runs
Example #29
0
def get_course_runs_for_course(course_uuid):
    catalog_integration = CatalogIntegration.current()

    if catalog_integration.is_enabled():
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            logger.error(
                'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
                catalog_integration.service_username,
            )
            return []

        api = create_catalog_api_client(user)
        cache_key = '{base}.course.{uuid}.course_runs'.format(
            base=catalog_integration.CACHE_KEY, uuid=course_uuid)
        data = get_edx_api_data(
            catalog_integration,
            'courses',
            resource_id=course_uuid,
            api=api,
            cache_key=cache_key
            if catalog_integration.is_cache_enabled else None,
            long_term_cache=True,
        )
        return data.get('course_runs', [])
    else:
        return []
Example #30
0
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 = dict()
    user, catalog_integration = check_catalog_integration_and_get_user(
        error_message_field='Data for course_run {}'.format(course_run_key))
    if user:
        api = create_catalog_api_client(user)

        cache_key = '{base}.course_runs'.format(
            base=catalog_integration.CACHE_KEY)

        course_run_details = get_edx_api_data(catalog_integration,
                                              'course_runs',
                                              api,
                                              resource_id=course_run_key,
                                              cache_key=cache_key,
                                              many=False,
                                              traverse_pagination=False,
                                              fields=fields)
    return course_run_details
Example #31
0
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.
    """
    no_data = []
    user_orders = []
    allowed_course_modes = ['professional', 'verified', 'credit']
    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
    api = ecommerce_api_client(user)
    commerce_user_orders = get_edx_api_data(commerce_configuration,
                                            user,
                                            'orders',
                                            api=api,
                                            querystring=user_query,
                                            cache_key=cache_key)

    for order in commerce_user_orders:
        if order['status'].lower() == 'complete':
            for line in order['lines']:
                product = line.get('product')
                if product:
                    for attribute in product['attribute_values']:
                        if attribute[
                                'name'] == 'certificate_type' and attribute[
                                    'value'] in allowed_course_modes:
                            try:
                                date_placed = datetime.strptime(
                                    order['date_placed'], "%Y-%m-%dT%H:%M:%SZ")
                                order_data = {
                                    'number':
                                    order['number'],
                                    'price':
                                    order['total_excl_tax'],
                                    'title':
                                    order['lines'][0]['title'],
                                    'order_date':
                                    strftime_localized(
                                        date_placed.replace(tzinfo=pytz.UTC),
                                        'SHORT_DATE'),
                                    'receipt_url':
                                    EcommerceService().get_receipt_page_url(
                                        order['number'])
                                }
                                user_orders.append(order_data)
                            except KeyError:
                                log.exception('Invalid order structure: %r',
                                              order)
                                return no_data

    return user_orders
Example #32
0
    def test_get_specific_resource(self):
        """Verify that a specific resource can be retrieved."""
        catalog_integration = self.create_catalog_integration()
        api = create_catalog_api_client(self.user)

        resource_id = 1
        url = '{api_root}/programs/{resource_id}/'.format(
            api_root=CatalogIntegration.current().get_internal_api_url().strip(
                '/'),
            resource_id=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_edx_api_data(catalog_integration,
                                           'programs',
                                           api=api,
                                           resource_id=resource_id)
        assert actual_resource == expected_resource

        self._assert_num_requests(1)
Example #33
0
def get_course_run(course_key, user):
    """Get a course run's data from the course catalog service.

    Arguments:
        course_key (CourseKey): Course key object identifying the run whose data we want.
        user (User): The user to authenticate as when making requests to the catalog service.

    Returns:
        dict, empty if no data could be retrieved.
    """
    catalog_integration = CatalogIntegration.current()

    scopes = ['email', 'profile']
    expires_in = settings.OAUTH_ID_TOKEN_EXPIRATION
    jwt = JwtBuilder(user).build_token(scopes, expires_in)
    api = EdxRestApiClient(catalog_integration.internal_api_url, jwt=jwt)

    data = get_edx_api_data(
        catalog_integration,
        user,
        'course_runs',
        resource_id=unicode(course_key),
        cache_key=catalog_integration.CACHE_KEY if catalog_integration.is_cache_enabled else None,
        api=api,
    )

    return data if data else {}
Example #34
0
    def test_get_specific_resource_with_falsey_id(self):
        """
        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 = create_catalog_api_client(self.user)

        resource_id = 0
        url = '{api_root}/programs/{resource_id}/'.format(
            api_root=CatalogIntegration.current().get_internal_api_url().strip(
                '/'),
            resource_id=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_edx_api_data(catalog_integration,
                                           'programs',
                                           api=api,
                                           resource_id=resource_id)
        assert actual_resource == expected_resource

        self._assert_num_requests(1)
Example #35
0
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.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            return []

        api = create_catalog_api_client(user)
        cache_key = '{base}.currency'.format(
            base=catalog_integration.CACHE_KEY)

        return get_edx_api_data(catalog_integration,
                                'currency',
                                api=api,
                                traverse_pagination=False,
                                cache_key=cache_key if
                                catalog_integration.is_cache_enabled else None)
    else:
        return []
Example #36
0
    def test_get_unpaginated_data(self):
        """Verify that unpaginated data can be retrieved."""
        catalog_integration = self.create_catalog_integration()
        api = create_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('edx_rest_api_client.client.EdxRestApiClient.__init__'
                        ) as mock_init:
            actual_collection = get_edx_api_data(catalog_integration,
                                                 'programs',
                                                 api=api)

            # 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."""
        program_config = self.create_programs_config()

        expected_collection = ['some', 'test', 'data']
        url = ProgramsApiConfig.current().internal_api_url.strip('/') + '/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_programs_api(responses)

        actual_collection = get_edx_api_data(program_config, self.user, 'programs')
        self.assertEqual(actual_collection, expected_collection)

        self._assert_num_requests(len(expected_collection))
Example #38
0
def get_course_runs():
    """
    Retrieve all the course runs from the catalog service.

    Returns:
        list of dict with each record representing a course run.
    """
    catalog_integration = CatalogIntegration.current()
    course_runs = []
    if catalog_integration.enabled:
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            logger.error(
                'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
                catalog_integration.service_username,
            )
            return course_runs

        api = create_catalog_api_client(user)

        querystring = {
            'page_size': catalog_integration.page_size,
            'exclude_utm': 1,
        }

        course_runs = get_edx_api_data(catalog_integration,
                                       'course_runs',
                                       api=api,
                                       querystring=querystring)

    return course_runs
Example #39
0
    def test_get_specific_resource(self):
        """Verify that a specific resource can be retrieved."""
        program_config = self.create_programs_config()

        resource_id = 1
        url = '{api_root}/programs/{resource_id}/'.format(
            api_root=ProgramsApiConfig.current().internal_api_url.strip('/'),
            resource_id=resource_id,
        )

        expected_resource = {'key': 'value'}

        self._mock_programs_api([
            httpretty.Response(body=json.dumps(expected_resource),
                               content_type='application/json')
        ],
                                url=url)

        actual_resource = get_edx_api_data(program_config,
                                           self.user,
                                           'programs',
                                           resource_id=resource_id)
        self.assertEqual(actual_resource, expected_resource)

        self._assert_num_requests(1)
Example #40
0
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:
        api = create_catalog_api_client(user)
        cache_key = '{base}.program_types'.format(base=catalog_integration.CACHE_KEY)

        data = get_edx_api_data(catalog_integration, 'program_types', api=api,
                                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 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
    api = ecommerce_api_client(user)
    commerce_user_orders = get_edx_api_data(
        commerce_configuration, 'orders', api=api, 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
Example #42
0
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
    """
    catalog_integration = CatalogIntegration.current()
    course_run_details = dict()
    if catalog_integration.enabled:
        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            msg = 'Catalog service user {} does not exist. Data for course_run {} will not be retrieved'.format(
                catalog_integration.service_username,
                course_run_key
            )
            logger.error(msg)
            return course_run_details
        api = create_catalog_api_client(user)

        cache_key = '{base}.course_runs'.format(base=catalog_integration.CACHE_KEY)

        course_run_details = get_edx_api_data(catalog_integration, 'course_runs', api, resource_id=course_run_key,
                                              cache_key=cache_key, many=False, traverse_pagination=False, fields=fields)
    else:
        msg = 'Unable to retrieve details about course_run {} because Catalog Integration is not enabled'.format(
            course_run_key
        )
        logger.error(msg)
    return course_run_details
Example #43
0
    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 = create_catalog_api_client(self.user)

        response = {'lang': 'en', 'weeks_to_complete': '5'}

        resource_id = 'course-v1:testX+testABC+1T2019'
        url = '{api_root}/course_runs/{resource_id}/'.format(
            api_root=CatalogIntegration.current().get_internal_api_url().strip(
                '/'),
            resource_id=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_edx_api_data(catalog_integration,
                                                    'course_runs',
                                                    resource_id=resource_id,
                                                    api=api,
                                                    cache_key=cache_key,
                                                    fields=['lang'])
        self.assertEqual(actual_resource_for_lang, expected_resource_for_lang)

        # Hit the cache
        actual_resource = get_edx_api_data(catalog_integration,
                                           'course_runs',
                                           api=api,
                                           resource_id=resource_id,
                                           cache_key=cache_key,
                                           fields=['weeks_to_complete'])

        self.assertEqual(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_edx_api_data(catalog_integration, 'programs', api=None)

        self.assertTrue(mock_warning.called)
        self.assertEqual(actual, [])
    def test_api_config_disabled(self, mock_warning):
        """Verify that no data is retrieved if the provided config model is disabled."""
        program_config = self.create_programs_config(enabled=False)

        actual = get_edx_api_data(program_config, self.user, 'programs')

        self.assertTrue(mock_warning.called)
        self.assertEqual(actual, [])
Example #46
0
    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_edx_api_data(catalog_integration, 'programs', api=None)

        assert mock_warning.called
        assert actual == []
    def test_api_config_disabled(self, mock_warning):
        """Verify that no data is retrieved if the provided config model is disabled."""
        program_config = self.create_programs_config(enabled=False)

        actual = get_edx_api_data(program_config, self.user, 'programs')

        self.assertTrue(mock_warning.called)
        self.assertEqual(actual, [])
Example #48
0
 def test_get_edx_api_data_retrieval_failure(self):
     """Verify exception is logged when data can't be retrieved from API."""
     program_config = self.create_programs_config()
     self.mock_programs_api(status_code=500)
     with LogCapture(LOGGER_NAME) as logger:
         actual = get_edx_api_data(program_config, self.user, "programs")
         logger.check((LOGGER_NAME, "ERROR", u"Failed to retrieve data from the programs API."))
         self.assertEqual(actual, [])
Example #49
0
    def test_get_edx_api_data_multiple_page(self):
        """Verify that all data is retrieve for multiple page response."""
        credentials_config = self.create_credentials_config()
        self.mock_credentials_api(self.user, is_next_page=True)
        querystring = {'username': self.user.username}

        actual = get_edx_api_data(credentials_config, self.user, 'user_credentials', querystring=querystring)
        expected_data = self.CREDENTIALS_NEXT_API_RESPONSE['results'] + self.CREDENTIALS_API_RESPONSE['results']
        self.assertEqual(actual, expected_data)
 def test_get_edx_api_data_retrieval_failure(self):
     """Verify exception is logged when data can't be retrieved from API."""
     program_config = self.create_programs_config()
     self.mock_programs_api(status_code=500)
     with LogCapture(LOGGER_NAME) as logger:
         actual = get_edx_api_data(program_config, self.user, 'programs')
         logger.check((LOGGER_NAME, 'ERROR',
                       u'Failed to retrieve data from the programs API.'))
         self.assertEqual(actual, [])
Example #51
0
    def test_get_edx_api_data_programs(self):
        """Verify programs data can be retrieved using get_edx_api_data."""
        program_config = self.create_programs_config()
        self.mock_programs_api()

        actual = get_edx_api_data(program_config, self.user, "programs")
        self.assertEqual(actual, self.PROGRAMS_API_RESPONSE["results"])

        # Verify the API was actually hit (not the cache).
        self.assertEqual(len(httpretty.httpretty.latest_requests), 1)
    def test_client_initialization_failure(self, mock_exception, mock_init):
        """Verify that an exception is logged when the API client fails to initialize."""
        mock_init.side_effect = Exception

        program_config = self.create_programs_config()

        actual = get_edx_api_data(program_config, self.user, 'programs')

        self.assertTrue(mock_exception.called)
        self.assertEqual(actual, [])
    def test_client_initialization_failure(self, mock_exception, mock_init):
        """Verify that an exception is logged when the API client fails to initialize."""
        mock_init.side_effect = Exception

        program_config = self.create_programs_config()

        actual = get_edx_api_data(program_config, self.user, 'programs')

        self.assertTrue(mock_exception.called)
        self.assertEqual(actual, [])
Example #54
0
def get_programs(uuid=None, types=None):  # pylint: disable=redefined-builtin
    """Retrieve marketable programs from the catalog service.

    Keyword Arguments:
        uuid (string): UUID identifying a specific program.
        types (list of string): List of program type names used to filter programs by type
                                (e.g., ["MicroMasters"] will only return MicroMasters programs,
                                ["MicroMasters", "XSeries"] will return MicroMasters and XSeries programs).

    Returns:
        list of dict, representing programs.
        dict, if a specific program is requested.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        try:
            user = User.objects.get(username=catalog_integration.service_username)
        except User.DoesNotExist:
            return []

        api = create_catalog_api_client(user, catalog_integration)
        types_param = ','.join(types) if types else None

        cache_key = '{base}.programs{types}'.format(
            base=catalog_integration.CACHE_KEY,
            types='.' + types_param if types_param else ''
        )

        querystring = {
            'exclude_utm': 1,
        }

        # TODO ECOM-7650: Remove this after https://github.com/edx/course-discovery/pull/805 is merged and released.
        if waffle.switch_is_active('display_retired_programs_on_learner_dashboard'):
            querystring['status'] = ('active', 'retired',)
        else:
            querystring['marketable'] = 1

        if uuid:
            querystring['use_full_course_serializer'] = 1

        if types_param:
            querystring['types'] = types_param

        return get_edx_api_data(
            catalog_integration,
            user,
            'programs',
            resource_id=uuid,
            cache_key=cache_key if catalog_integration.is_cache_enabled else None,
            api=api,
            querystring=querystring,
        )
    else:
        return []
Example #55
0
    def test_get_edx_api_data_client_initialization_failure(self, mock_init):
        """Verify no data is retrieved and exception logged when API client
        fails to initialize.
        """
        program_config = self.create_programs_config()
        mock_init.side_effect = Exception

        with LogCapture(LOGGER_NAME) as logger:
            actual = get_edx_api_data(program_config, self.user, "programs")
            logger.check((LOGGER_NAME, "ERROR", u"Failed to initialize the programs API client."))
            self.assertEqual(actual, [])
            self.assertTrue(mock_init.called)
    def test_data_retrieval_failure(self, mock_exception):
        """Verify that an exception is logged when data can't be retrieved."""
        program_config = self.create_programs_config()

        self._mock_programs_api(
            [httpretty.Response(body='clunk', content_type='application/json', status_code=500)]
        )

        actual = get_edx_api_data(program_config, self.user, 'programs')

        self.assertTrue(mock_exception.called)
        self.assertEqual(actual, [])
    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 = create_catalog_api_client(self.user)

        self._mock_catalog_api(
            [httpretty.Response(body='clunk', content_type='application/json', status_code=500)]
        )

        actual = get_edx_api_data(catalog_integration, 'programs', api=api)

        self.assertTrue(mock_exception.called)
        self.assertEqual(actual, [])
    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 = create_catalog_api_client(self.user)

        response = {'lang': 'en', 'weeks_to_complete': '5'}

        resource_id = 'course-v1:testX+testABC+1T2019'
        url = '{api_root}/course_runs/{resource_id}/'.format(
            api_root=CatalogIntegration.current().get_internal_api_url().strip('/'),
            resource_id=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_edx_api_data(
            catalog_integration, 'course_runs', resource_id=resource_id, api=api, cache_key=cache_key, fields=['lang']
        )
        self.assertEqual(actual_resource_for_lang, expected_resource_for_lang)

        # Hit the cache
        actual_resource = get_edx_api_data(
            catalog_integration, 'course_runs', api=api, resource_id=resource_id, cache_key=cache_key,
            fields=['weeks_to_complete']
        )

        self.assertEqual(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_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_edx_api_data(
            catalog_integration,
            'programs',
            api=None,
            resource_id=100,
            many=False
        )

        self.assertTrue(mock_warning.called)
        self.assertEqual(actual, {})