Пример #1
0
    def test_with_required(self):
        ''' Test with required arguments supplied'''

        initial = CatalogIntegration.current()

        # test with both required args
        call_command(
            "create_catalog_integrations",
            "--internal_api_url", self.catalog_integration_defaults['internal_api_url'],
            "--service_username", self.catalog_integration_defaults['service_username']
        )

        current = CatalogIntegration.current()

        # assert current has changed
        self.assertNotEqual(
            initial,
            current
        )

        self.assertEqual(
            current.enabled,
            False
        )
        self.assertEqual(
            current.internal_api_url,
            self.catalog_integration_defaults['internal_api_url']
        )

        self.assertEqual(
            current.service_username,
            self.catalog_integration_defaults['service_username']
        )
Пример #2
0
    def test_cache_utilization(self):
        """Verify that when enabled, the cache is used."""
        catalog_integration = self.create_catalog_integration(cache_ttl=5)
        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')
        ], )

        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)

        cache_key = CatalogIntegration.current().CACHE_KEY

        # Warm up the cache.
        get_edx_api_data(catalog_integration,
                         'programs',
                         api=api,
                         cache_key=cache_key)
        get_edx_api_data(catalog_integration,
                         'programs',
                         api=api,
                         resource_id=resource_id,
                         cache_key=cache_key)

        # Hit the cache.
        actual_collection = get_edx_api_data(catalog_integration,
                                             'programs',
                                             api=api,
                                             cache_key=cache_key)
        assert actual_collection == expected_collection

        actual_resource = get_edx_api_data(catalog_integration,
                                           'programs',
                                           api=api,
                                           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)
Пример #3
0
    def create_catalog_integration(self, **kwargs):
        """
        Creates a new CatalogIntegration with catalog_integration_defaults,
        updated with any provided overrides.
        """
        fields = dict(self.catalog_integration_defaults, **kwargs)
        CatalogIntegration(**fields).save()

        return CatalogIntegration.current()
Пример #4
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 {}
Пример #5
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 []
Пример #6
0
def check_catalog_integration_and_get_user(error_message_field):
    """
    Checks that catalog integration is enabled, and if so, attempts to get and
    return the service user.

    Parameters:
        error_message_field (str): The field that will be attempted to be
            retrieved when calling the api client. Used for the error message.

    Returns:
        Tuple of:
            The catalog integration service user if it exists, else None
            The catalog integration Object
                Note: (This is necessary for future calls of functions using this method)
    """
    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 [{username}] does not exist. '
                '{field} will not be retrieved.'.format(
                    username=catalog_integration.service_username,
                    field=error_message_field,
                ))
            return None, catalog_integration
        return user, catalog_integration
    else:
        logger.error(
            'Unable to retrieve details about {field} because Catalog Integration is not enabled'
            .format(field=error_message_field, ))
        return None, catalog_integration
Пример #7
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 {}
Пример #8
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 {}
Пример #9
0
    def __init__(self):
        """
        Initialize an authenticated Discovery service API client by using the
        provided user.
        """
        catalog_integration = CatalogIntegration.current()

        # Client can't be used if there is no catalog integration
        if not (catalog_integration and catalog_integration.enabled):
            LOGGER.error(
                "Unable to create DiscoveryApiClient because catalog integration not set up or enabled"
            )
            return None

        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            LOGGER.error("Unable to retrieve catalog integration service user")
            return None

        jwt = JwtBuilder(user).build_token([])
        base_url = configuration_helpers.get_value(
            'COURSE_CATALOG_URL_BASE', settings.COURSE_CATALOG_URL_BASE)
        self.client = EdxRestApiClient('{base_url}{journals_path}'.format(
            base_url=base_url, journals_path=JOURNALS_API_PATH),
                                       jwt=jwt)
Пример #10
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 []
Пример #11
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)
Пример #12
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))
Пример #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,
        )

        return data if data else {}
    else:
        return {}
Пример #14
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 []
Пример #15
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 []
Пример #16
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)
Пример #17
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
Пример #18
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
Пример #19
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
Пример #20
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)
Пример #21
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 []
Пример #22
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 []
Пример #23
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 []
    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)
Пример #25
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
Пример #26
0
def get_program(program_uuid, ignore_cache=False):
    """
    Retrieves the details for the specified program.

     Args:
         program_uuid (UUID): Program identifier
         ignore_cache (bool): Indicates if previously-cached data should be ignored.

     Returns:
         dict
    """
    program_uuid = str(program_uuid)
    cache_key = 'programs.api.data.{uuid}'.format(uuid=program_uuid)

    if not ignore_cache:
        program = cache.get(cache_key)

        if program:
            return program

    catalog_integration = CatalogIntegration.current()
    user = catalog_integration.get_service_user()
    api = create_catalog_api_client(user)

    program = api.programs(program_uuid).get()
    cache.set(cache_key, program, getattr(settings, 'PROGRAMS_CACHE_TTL', 60))

    return program
    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)
        self.assertEqual(actual_collection, expected_collection)

        self._assert_num_requests(len(expected_collection))
    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)
Пример #29
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
    def test_with_optional(self):
        ''' Test with optionals arguments supplied'''
        initial = CatalogIntegration.current()

        # test --enabled
        call_command(
            "create_catalog_integrations",
            "--internal_api_url", self.catalog_integration_defaults['internal_api_url'],
            "--service_username", self.catalog_integration_defaults['service_username'],
            "--enabled"
        )

        current = CatalogIntegration.current()

        # assert current has changed
        assert initial != current

        assert current.enabled is True
        assert current.internal_api_url == self.catalog_integration_defaults['internal_api_url']

        assert current.service_username == self.catalog_integration_defaults['service_username']

        # test with all args
        call_command(
            "create_catalog_integrations",
            "--internal_api_url", self.catalog_integration_defaults['internal_api_url'],
            "--service_username", self.catalog_integration_defaults['service_username'],
            "--enabled",
            "--cache_ttl", 500,
            "--long_term_cache_ttl", 500,
            "--page_size", 500
        )

        current = CatalogIntegration.current()

        # assert current has changed
        assert initial != current

        assert current.enabled is True
        assert current.internal_api_url == self.catalog_integration_defaults['internal_api_url']

        assert current.service_username == self.catalog_integration_defaults['service_username']

        assert current.cache_ttl == 500

        assert current.long_term_cache_ttl == 500
        assert current.page_size == 500
    def test_with_optional(self):
        ''' Test with optionals arguments supplied'''
        initial = CatalogIntegration.current()

        # test --enabled
        call_command("create_catalog_integrations", "--internal_api_url",
                     self.catalog_integration_defaults['internal_api_url'],
                     "--service_username",
                     self.catalog_integration_defaults['service_username'],
                     "--enabled")

        current = CatalogIntegration.current()

        # assert current has changed
        self.assertNotEqual(initial, current)

        self.assertEqual(current.enabled, True)
        self.assertEqual(current.internal_api_url,
                         self.catalog_integration_defaults['internal_api_url'])

        self.assertEqual(current.service_username,
                         self.catalog_integration_defaults['service_username'])

        # test with all args
        call_command("create_catalog_integrations", "--internal_api_url",
                     self.catalog_integration_defaults['internal_api_url'],
                     "--service_username",
                     self.catalog_integration_defaults['service_username'],
                     "--enabled", "--cache_ttl", 500, "--long_term_cache_ttl",
                     500, "--page_size", 500)

        current = CatalogIntegration.current()

        # assert current has changed
        self.assertNotEqual(initial, current)

        self.assertEqual(current.enabled, True)
        self.assertEqual(current.internal_api_url,
                         self.catalog_integration_defaults['internal_api_url'])

        self.assertEqual(current.service_username,
                         self.catalog_integration_defaults['service_username'])

        self.assertEqual(current.cache_ttl, 500)

        self.assertEqual(current.long_term_cache_ttl, 500)
        self.assertEqual(current.page_size, 500)
Пример #32
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)
Пример #33
0
    def test_cache_utilization(self):
        """Verify that when enabled, the cache is used."""
        catalog_integration = self.create_catalog_integration(cache_ttl=5)
        api = create_catalog_api_client(self.user, catalog_integration)

        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 = '{api_root}/programs/{resource_id}/'.format(
            api_root=CatalogIntegration.current().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
        )

        cache_key = CatalogIntegration.current().CACHE_KEY

        # Warm up the cache.
        get_edx_api_data(catalog_integration, self.user, 'programs', api=api, cache_key=cache_key)
        get_edx_api_data(
            catalog_integration, self.user, 'programs', api=api, resource_id=resource_id, cache_key=cache_key
        )

        # Hit the cache.
        actual_collection = get_edx_api_data(catalog_integration, self.user, 'programs', api=api, cache_key=cache_key)
        self.assertEqual(actual_collection, expected_collection)

        actual_resource = get_edx_api_data(
            catalog_integration, self.user, 'programs', api=api, resource_id=resource_id, cache_key=cache_key
        )
        self.assertEqual(actual_resource, expected_resource)

        # Verify that only two requests were made, not four.
        self._assert_num_requests(2)
Пример #34
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.
    """
    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 UUID will not be retrieved.',
                catalog_integration.service_username,
            )
            return []

        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
Пример #35
0
    def handle(self, *args, **options):
        catalog_integration = CatalogIntegration.current()
        username = catalog_integration.service_username

        try:
            user = User.objects.get(username=username)
            client = create_catalog_api_client(user)
        except User.DoesNotExist:
            logger.error(
                'Failed to create API client. Service user {username} does not exist.'
                .format(username))
            raise

        try:
            querystring = {
                'exclude_utm': 1,
                'status': ('active', 'retired'),
                'uuids_only': 1,
            }

            logger.info('Requesting program UUIDs.')
            uuids = client.programs.get(**querystring)
        except:  # pylint: disable=bare-except
            logger.error('Failed to retrieve program UUIDs.')
            raise

        total = len(uuids)
        logger.info('Received {total} UUIDs.'.format(total=total))

        programs = {}
        failure = False
        for uuid in uuids:
            try:
                logger.info(
                    'Requesting details for program {uuid}.'.format(uuid=uuid))
                program = client.programs(uuid).get(exclude_utm=1)

                cache_key = PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)
                programs[cache_key] = program
            except:  # pylint: disable=bare-except
                logger.exception(
                    'Failed to retrieve details for program {uuid}.'.format(
                        uuid=uuid))
                failure = True

                continue

        successful = len(programs)
        logger.info('Caching details for {successful} programs.'.format(
            successful=successful))
        cache.set_many(programs, None)

        logger.info('Caching UUIDs for {total} programs.'.format(total=total))
        cache.set(PROGRAM_UUIDS_CACHE_KEY, uuids, None)

        if failure:
            # This will fail a Jenkins job running this command, letting site
            # operators know that there was a problem.
            sys.exit(1)
Пример #36
0
    def create_catalog_integration(self, **kwargs):
        """
        Creates a new CatalogIntegration with DEFAULTS, updated with any provided overrides.
        """
        fields = dict(self.DEFAULTS, **kwargs)
        CatalogIntegration(**fields).save()

        return CatalogIntegration.current()
Пример #37
0
def create_catalog_api_client(user):
    """Returns an API client which can be used to make Catalog API requests."""
    scopes = ['email', 'profile']
    expires_in = settings.OAUTH_ID_TOKEN_EXPIRATION
    jwt = JwtBuilder(user).build_token(scopes, expires_in)

    url = CatalogIntegration.current().get_internal_api_url()
    return EdxRestApiClient(url, jwt=jwt)
Пример #38
0
def get_catalog_api_base_url(site=None):
    """
    Returns a base API url used to make Catalog API requests.
    """
    if site:
        return site.configuration.get_value('COURSE_CATALOG_API_URL')

    return CatalogIntegration.current().get_internal_api_url()
Пример #39
0
    def _mock_catalog_api(self, responses, url=None):
        assert httpretty.is_enabled(
        ), 'httpretty must be enabled to mock Catalog API calls.'

        url = url if url else CatalogIntegration.current(
        ).get_internal_api_url().strip('/') + '/programs/'

        httpretty.register_uri(httpretty.GET, url, responses=responses)
Пример #40
0
    def setUp(self):
        super().setUp()

        self.user = UserFactory()
        self.base_api_url = CatalogIntegration.current().get_internal_api_url(
        ).strip('/')

        httpretty.httpretty.reset()
        cache.clear()
Пример #41
0
 def cache_course_run(cls, catalog_course_run):
     """
     Caches catalog course run for course key.
     """
     cache.set(
         cls._get_cache_key_name(catalog_course_run["key"]),
         catalog_course_run,
         CatalogIntegration.current().cache_ttl
     )
Пример #42
0
    def handle(self, *args, **options):
        failure = False
        logger.info('populate-multitenant-programs switch is ON')

        catalog_integration = CatalogIntegration.current()
        username = catalog_integration.service_username

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            logger.error(
                'Failed to create API client. Service user {username} does not exist.'
                .format(username=username))
            raise

        programs = {}
        for site in Site.objects.all():
            site_config = getattr(site, 'configuration', None)
            if site_config is None or not site_config.get_value(
                    'COURSE_CATALOG_API_URL'):
                logger.info('Skipping site {domain}. No configuration.'.format(
                    domain=site.domain))
                cache.set(
                    SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(
                        domain=site.domain), [], None)
                continue

            client = create_catalog_api_client(user, site=site)
            uuids, program_uuids_failed = self.get_site_program_uuids(
                client, site)
            new_programs, program_details_failed = self.fetch_program_details(
                client, uuids)

            if program_uuids_failed or program_details_failed:
                failure = True

            programs.update(new_programs)

            logger.info(
                'Caching UUIDs for {total} programs for site {site_name}.'.
                format(
                    total=len(uuids),
                    site_name=site.domain,
                ))
            cache.set(
                SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=site.domain),
                uuids, None)

        successful = len(programs)
        logger.info('Caching details for {successful} programs.'.format(
            successful=successful))
        cache.set_many(programs, None)

        if failure:
            # This will fail a Jenkins job running this command, letting site
            # operators know that there was a problem.
            sys.exit(1)
Пример #43
0
def create_catalog_api_client(user, site=None):
    """Returns an API client which can be used to make Catalog API requests."""
    jwt = create_jwt_for_user(user)

    if site:
        url = site.configuration.get_value('COURSE_CATALOG_API_URL')
    else:
        url = CatalogIntegration.current().get_internal_api_url()

    return EdxRestApiClient(url, jwt=jwt)
Пример #44
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 []
Пример #45
0
def create_catalog_api_client(user, site=None):
    """Returns an API client which can be used to make Catalog API requests."""
    scopes = ['email', 'profile']
    expires_in = settings.OAUTH_ID_TOKEN_EXPIRATION
    jwt = JwtBuilder(user).build_token(scopes, expires_in)

    if site:
        url = site.configuration.get_value('COURSE_CATALOG_API_URL')
    else:
        url = CatalogIntegration.current().get_internal_api_url()

    return EdxRestApiClient(url, jwt=jwt)
    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)
Пример #47
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 = {
            'marketable': 1,
            'exclude_utm': 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 []
Пример #48
0
def bundle_about(request, bundle_uuid):
    """
    Journal bundle about page's view.
    """
    bundle = get_journal_bundles(request.site, bundle_uuid=bundle_uuid)
    if not bundle:
        raise Http404
    bundle = bundle[0]  # get_journal_bundles always returns list of bundles
    bundle = extend_bundle(bundle)
    context = {
        'journals_root_url': get_journals_root_url(),
        'discovery_root_url': CatalogIntegration.current().get_internal_api_url(),
        'bundle': bundle,
        'uses_bootstrap': True,
    }
    return render_to_response('journals/bundle_about.html', context)
Пример #49
0
def is_course_in_enterprise_catalog(site, course_id, enterprise_catalog_id):
    """
    Verify that the provided course id exists in the site base list of course
    run keys from the provided enterprise course catalog.

    Arguments:
        course_id (str): The course ID.
        site: (django.contrib.sites.Site) site instance
        enterprise_catalog_id (Int): Course catalog id of enterprise

    Returns:
        Boolean

    """
    cache_key = get_cache_key(
        site_domain=site.domain,
        resource='catalogs.contains',
        course_id=course_id,
        catalog_id=enterprise_catalog_id
    )
    response = cache.get(cache_key)
    if not response:
        catalog_integration = CatalogIntegration.current()
        if not catalog_integration.enabled:
            LOGGER.error("Catalog integration is not enabled.")
            return False

        try:
            user = User.objects.get(username=catalog_integration.service_username)
        except User.DoesNotExist:
            LOGGER.exception("Catalog service user '%s' does not exist.", catalog_integration.service_username)
            return False

        try:
            # GET: /api/v1/catalogs/{catalog_id}/contains?course_run_id={course_run_ids}
            response = create_catalog_api_client(user=user).catalogs(enterprise_catalog_id).contains.get(
                course_run_id=course_id
            )
            cache.set(cache_key, response, settings.COURSES_API_CACHE_TIMEOUT)
        except (ConnectionError, SlumberBaseException, Timeout):
            LOGGER.exception('Unable to connect to Course Catalog service for catalog contains endpoint.')
            return False

    try:
        return response['courses'][course_id]
    except KeyError:
        return False
Пример #50
0
    def handle(self, *args, **options):
        failure = False
        logger.info('populate-multitenant-programs switch is ON')

        catalog_integration = CatalogIntegration.current()
        username = catalog_integration.service_username

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            logger.error(
                'Failed to create API client. Service user {username} does not exist.'.format(username=username)
            )
            raise

        programs = {}
        for site in Site.objects.all():
            site_config = getattr(site, 'configuration', None)
            if site_config is None or not site_config.get_value('COURSE_CATALOG_API_URL'):
                logger.info('Skipping site {domain}. No configuration.'.format(domain=site.domain))
                cache.set(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=site.domain), [], None)
                continue

            client = create_catalog_api_client(user, site=site)
            uuids, program_uuids_failed = self.get_site_program_uuids(client, site)
            new_programs, program_details_failed = self.fetch_program_details(client, uuids)

            if program_uuids_failed or program_details_failed:
                failure = True

            programs.update(new_programs)

            logger.info('Caching UUIDs for {total} programs for site {site_name}.'.format(
                total=len(uuids),
                site_name=site.domain,
            ))
            cache.set(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=site.domain), uuids, None)

        successful = len(programs)
        logger.info('Caching details for {successful} programs.'.format(successful=successful))
        cache.set_many(programs, None)

        if failure:
            # This will fail a Jenkins job running this command, letting site
            # operators know that there was a problem.
            sys.exit(1)
Пример #51
0
def get_programs(user=None, uuid=None, type=None):  # pylint: disable=redefined-builtin
    """Retrieve marketable programs from the catalog service.

    Keyword Arguments:
        uuid (string): UUID identifying a specific program.
        type (string): Filter programs by type (e.g., "MicroMasters" will only return MicroMasters programs).

    Returns:
        list of dict, representing programs.
        dict, if a specific program is requested.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        user = _get_service_user(user, catalog_integration.service_username)
        if not user:
            return []

        api = create_catalog_api_client(user, catalog_integration)

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

        querystring = {
            'marketable': 1,
            'exclude_utm': 1,
            'published_course_runs_only': 1,
        }
        if type:
            querystring['type'] = type

        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 []
Пример #52
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,
                                cache_key=cache_key if catalog_integration.is_cache_enabled else None)
    else:
        return []
Пример #53
0
def get_and_cache_course_runs(course_keys, user):
    """
    Get course run's data from the course catalog service and cache it.
    """
    catalog_course_runs_against_course_keys = {}
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        api = create_catalog_api_client(user, catalog_integration)

        catalog_data = get_edx_api_data(
            catalog_integration,
            user,
            'course_runs',
            api=api,
            querystring={'keys': ",".join(course_keys), 'exclude_utm': 1},
        )
        if catalog_data:
            for catalog_course_run in catalog_data:
                CatalogCacheUtility.cache_course_run(catalog_course_run)
                catalog_course_runs_against_course_keys[catalog_course_run["key"]] = catalog_course_run
    return catalog_course_runs_against_course_keys
    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)
        self.assertEqual(actual_resource, expected_resource)

        self._assert_num_requests(1)
Пример #55
0
def check_catalog_integration_and_get_user(error_message_field):
    """
    Checks that catalog integration is enabled, and if so, attempts to get and
    return the service user.

    Parameters:
        error_message_field (str): The field that will be attempted to be
            retrieved when calling the api client. Used for the error message.

    Returns:
        Tuple of:
            The catalog integration service user if it exists, else None
            The catalog integration Object
                Note: (This is necessary for future calls of functions using this method)
    """
    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 [{username}] does not exist. '
                '{field} will not be retrieved.'.format(
                    username=catalog_integration.service_username,
                    field=error_message_field,
                )
            )
            return None, catalog_integration
        return user, catalog_integration
    else:
        logger.error(
            'Unable to retrieve details about {field} because Catalog Integration is not enabled'.format(
                field=error_message_field,
            )
        )
        return None, catalog_integration
Пример #56
0
def get_program_types(user=None):  # pylint: disable=redefined-builtin
    """Retrieve all program types from the catalog service.

    Returns:
        list of dict, representing program types.
    """
    catalog_integration = CatalogIntegration.current()
    if catalog_integration.enabled:
        user = _get_service_user(user, catalog_integration.service_username)
        if not user:
            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 []
Пример #57
0
    def __init__(self):
        """
        Initialize an authenticated Discovery service API client by using the
        provided user.
        """
        catalog_integration = CatalogIntegration.current()

        # Client can't be used if there is no catalog integration
        if not (catalog_integration and catalog_integration.enabled):
            LOGGER.error("Unable to create DiscoveryApiClient because catalog integration not set up or enabled")
            return None

        try:
            user = catalog_integration.get_service_user()
        except ObjectDoesNotExist:
            LOGGER.error("Unable to retrieve catalog integration service user")
            return None

        jwt = JwtBuilder(user).build_token([])
        base_url = configuration_helpers.get_value('COURSE_CATALOG_URL_BASE', settings.COURSE_CATALOG_URL_BASE)
        self.client = EdxRestApiClient(
            '{base_url}{journals_path}'.format(base_url=base_url, journals_path=JOURNALS_API_PATH),
            jwt=jwt
        )
    def test_with_optional(self):
        ''' Test with optionals arguments supplied'''
        initial = CatalogIntegration.current()

        # test --enabled
        call_command(
            "create_catalog_integrations",
            "--internal_api_url", self.catalog_integration_defaults['internal_api_url'],
            "--service_username", self.catalog_integration_defaults['service_username'],
            "--enabled"
        )

        current = CatalogIntegration.current()

        # assert current has changed
        self.assertNotEqual(
            initial,
            current
        )

        self.assertEqual(
            current.enabled,
            True
        )
        self.assertEqual(
            current.internal_api_url,
            self.catalog_integration_defaults['internal_api_url']
        )

        self.assertEqual(
            current.service_username,
            self.catalog_integration_defaults['service_username']
        )

        # test with all args
        call_command(
            "create_catalog_integrations",
            "--internal_api_url", self.catalog_integration_defaults['internal_api_url'],
            "--service_username", self.catalog_integration_defaults['service_username'],
            "--enabled",
            "--cache_ttl", 500,
            "--long_term_cache_ttl", 500,
            "--page_size", 500
        )

        current = CatalogIntegration.current()

        # assert current has changed
        self.assertNotEqual(
            initial,
            current
        )

        self.assertEqual(
            current.enabled,
            True
        )
        self.assertEqual(
            current.internal_api_url,
            self.catalog_integration_defaults['internal_api_url']
        )

        self.assertEqual(
            current.service_username,
            self.catalog_integration_defaults['service_username']
        )

        self.assertEqual(
            current.cache_ttl,
            500
        )

        self.assertEqual(
            current.long_term_cache_ttl,
            500
        )
        self.assertEqual(
            current.page_size,
            500
        )
Пример #59
0
    def handle(self, *args, **options):
        if waffle.switch_is_active('populate-multitenant-programs'):
            failure = False
            logger.info('populate-multitenant-programs switch is ON')

            catalog_integration = CatalogIntegration.current()
            username = catalog_integration.service_username

            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                logger.error(
                    'Failed to create API client. Service user {username} does not exist.'.format(username)
                )
                raise

            programs = {}
            for site in Site.objects.all():
                site_config = getattr(site, 'configuration', None)
                if site_config is None or not site_config.get_value('COURSE_CATALOG_API_URL'):
                    logger.info('Skipping site {domain}. No configuration.'.format(domain=site.domain))
                    continue

                client = create_catalog_api_client(user, site=site)
                uuids, program_uuids_failed = self.get_site_program_uuids(client, site)
                new_programs, program_details_failed = self.fetch_program_details(client, uuids)

                if program_uuids_failed or program_details_failed:
                    failure = True

                programs.update(new_programs)

                logger.info('Caching UUIDs for {total} programs for site {site_name}.'.format(
                    total=len(uuids),
                    site_name=site.domain,
                ))
                cache.set(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=site.domain), uuids, None)

            successful = len(programs)
            logger.info('Caching details for {successful} programs.'.format(successful=successful))
            cache.set_many(programs, None)

            if failure:
                # This will fail a Jenkins job running this command, letting site
                # operators know that there was a problem.
                sys.exit(1)

        else:
            catalog_integration = CatalogIntegration.current()
            username = catalog_integration.service_username

            try:
                user = User.objects.get(username=username)
                client = create_catalog_api_client(user)
            except User.DoesNotExist:
                logger.error(
                    'Failed to create API client. Service user {username} does not exist.'.format(username)
                )
                raise

            try:
                querystring = {
                    'exclude_utm': 1,
                    'status': ('active', 'retired'),
                    'uuids_only': 1,
                }

                logger.info('Requesting program UUIDs.')
                uuids = client.programs.get(**querystring)
            except:  # pylint: disable=bare-except
                logger.error('Failed to retrieve program UUIDs.')
                raise

            total = len(uuids)
            logger.info('Received {total} UUIDs.'.format(total=total))

            programs = {}
            failure = False
            for uuid in uuids:
                try:
                    logger.info('Requesting details for program {uuid}.'.format(uuid=uuid))
                    program = client.programs(uuid).get(exclude_utm=1)

                    cache_key = PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)
                    programs[cache_key] = program
                except:  # pylint: disable=bare-except
                    logger.exception('Failed to retrieve details for program {uuid}.'.format(uuid=uuid))
                    failure = True

                    continue

            successful = len(programs)
            logger.info('Caching details for {successful} programs.'.format(successful=successful))
            cache.set_many(programs, None)

            logger.info('Caching UUIDs for {total} programs.'.format(total=total))
            cache.set(PROGRAM_UUIDS_CACHE_KEY, uuids, None)

            if failure:
                # This will fail a Jenkins job running this command, letting site
                # operators know that there was a problem.
                sys.exit(1)