Commit 78a9b1f0 by Renzo Lucioni

Remove use of read_cached_programs switch

The read_cached_programs switch was used to control the release of changes for reading programs exclusively from the cache. With those changes stable, the switch is no longer necessary.

LEARNER-382
parent 278fea9d
...@@ -13,23 +13,34 @@ class CatalogFixture(object): ...@@ -13,23 +13,34 @@ class CatalogFixture(object):
""" """
Interface to set up mock responses from the Catalog stub server. Interface to set up mock responses from the Catalog stub server.
""" """
def install_programs(self, data): def install_programs(self, programs):
"""Set response data for the catalog's course run API.""" """
Stub the discovery service's program list and detail API endpoints.
Arguments:
programs (list): A list of programs. Both list and detail endpoints
will be stubbed using data from this list.
"""
key = 'catalog.programs' key = 'catalog.programs'
if isinstance(data, dict): uuids = []
key += '.' + data['uuid'] for program in programs:
uuid = program['uuid']
uuids.append(uuid)
program_key = '{base}.{uuid}'.format(base=key, uuid=uuid)
requests.put( requests.put(
'{}/set_config'.format(CATALOG_STUB_URL), '{}/set_config'.format(CATALOG_STUB_URL),
data={key: json.dumps(data)}, data={program_key: json.dumps(program)},
)
else:
requests.put(
'{}/set_config'.format(CATALOG_STUB_URL),
data={key: json.dumps({'results': data})},
) )
# Stub list endpoint as if the uuids_only query param had been passed.
requests.put(
'{}/set_config'.format(CATALOG_STUB_URL),
data={key: json.dumps(uuids)},
)
class CatalogIntegrationMixin(object): class CatalogIntegrationMixin(object):
"""Mixin providing a method used to configure the catalog integration.""" """Mixin providing a method used to configure the catalog integration."""
......
import re
from bok_choy.page_object import PageObject
from common.test.acceptance.pages.lms import BASE_URL
class CacheProgramsPage(PageObject):
"""
Visit this page to call the cache_programs management command.
This page makes a GET request to a view which is only meant to be enabled in
testing contexts where the LMS can only be reached over HTTP. Stub the
discovery service before visiting this page.
"""
url = BASE_URL + '/catalog/management/cache_programs/'
def is_browser_on_page(self):
body = self.q(css='body').text[0]
match = re.search(r'programs cached', body, flags=re.IGNORECASE)
return True if match else False
...@@ -6,6 +6,7 @@ from common.test.acceptance.fixtures.programs import ProgramsConfigMixin ...@@ -6,6 +6,7 @@ from common.test.acceptance.fixtures.programs import ProgramsConfigMixin
from common.test.acceptance.fixtures.course import CourseFixture from common.test.acceptance.fixtures.course import CourseFixture
from common.test.acceptance.tests.helpers import UniqueCourseTest from common.test.acceptance.tests.helpers import UniqueCourseTest
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from common.test.acceptance.pages.lms.catalog import CacheProgramsPage
from common.test.acceptance.pages.lms.programs import ProgramListingPage, ProgramDetailsPage from common.test.acceptance.pages.lms.programs import ProgramListingPage, ProgramDetailsPage
from openedx.core.djangoapps.catalog.tests.factories import ( from openedx.core.djangoapps.catalog.tests.factories import (
ProgramFactory, CourseFactory, CourseRunFactory ProgramFactory, CourseFactory, CourseRunFactory
...@@ -39,10 +40,19 @@ class ProgramPageBase(ProgramsConfigMixin, CatalogIntegrationMixin, UniqueCourse ...@@ -39,10 +40,19 @@ class ProgramPageBase(ProgramsConfigMixin, CatalogIntegrationMixin, UniqueCourse
return ProgramFactory(courses=[course]) return ProgramFactory(courses=[course])
def stub_catalog_api(self, data=None): def stub_catalog_api(self, programs):
"""Stub out the catalog API's program and course run endpoints.""" """
Stub the discovery service's program list and detail API endpoints.
"""
self.set_catalog_integration(is_enabled=True, service_username=self.username) self.set_catalog_integration(is_enabled=True, service_username=self.username)
CatalogFixture().install_programs(data or self.programs) CatalogFixture().install_programs(programs)
def cache_programs(self):
"""
Populate the LMS' cache of program data.
"""
cache_programs_page = CacheProgramsPage(self.browser)
cache_programs_page.visit()
class ProgramListingPageTest(ProgramPageBase): class ProgramListingPageTest(ProgramPageBase):
...@@ -55,7 +65,8 @@ class ProgramListingPageTest(ProgramPageBase): ...@@ -55,7 +65,8 @@ class ProgramListingPageTest(ProgramPageBase):
def test_no_enrollments(self): def test_no_enrollments(self):
"""Verify that no cards appear when the user has no enrollments.""" """Verify that no cards appear when the user has no enrollments."""
self.auth(enroll=False) self.auth(enroll=False)
self.stub_catalog_api() self.stub_catalog_api(self.programs)
self.cache_programs()
self.listing_page.visit() self.listing_page.visit()
...@@ -68,7 +79,8 @@ class ProgramListingPageTest(ProgramPageBase): ...@@ -68,7 +79,8 @@ class ProgramListingPageTest(ProgramPageBase):
but none are included in an active program. but none are included in an active program.
""" """
self.auth() self.auth()
self.stub_catalog_api() self.stub_catalog_api(self.programs)
self.cache_programs()
self.listing_page.visit() self.listing_page.visit()
...@@ -83,7 +95,8 @@ class ProgramListingPageTest(ProgramPageBase): ...@@ -83,7 +95,8 @@ class ProgramListingPageTest(ProgramPageBase):
self.auth() self.auth()
program = self.create_program() program = self.create_program()
self.stub_catalog_api(data=[program]) self.stub_catalog_api(programs=[program])
self.cache_programs()
self.listing_page.visit() self.listing_page.visit()
...@@ -104,7 +117,8 @@ class ProgramListingPageA11yTest(ProgramPageBase): ...@@ -104,7 +117,8 @@ class ProgramListingPageA11yTest(ProgramPageBase):
def test_empty_a11y(self): def test_empty_a11y(self):
"""Test a11y of the page's empty state.""" """Test a11y of the page's empty state."""
self.auth(enroll=False) self.auth(enroll=False)
self.stub_catalog_api(data=[self.program]) self.stub_catalog_api(programs=[self.program])
self.cache_programs()
self.listing_page.visit() self.listing_page.visit()
...@@ -115,7 +129,8 @@ class ProgramListingPageA11yTest(ProgramPageBase): ...@@ -115,7 +129,8 @@ class ProgramListingPageA11yTest(ProgramPageBase):
def test_cards_a11y(self): def test_cards_a11y(self):
"""Test a11y when program cards are present.""" """Test a11y when program cards are present."""
self.auth() self.auth()
self.stub_catalog_api(data=[self.program]) self.stub_catalog_api(programs=[self.program])
self.cache_programs()
self.listing_page.visit() self.listing_page.visit()
...@@ -138,7 +153,8 @@ class ProgramDetailsPageA11yTest(ProgramPageBase): ...@@ -138,7 +153,8 @@ class ProgramDetailsPageA11yTest(ProgramPageBase):
def test_a11y(self): def test_a11y(self):
"""Test the page's a11y compliance.""" """Test the page's a11y compliance."""
self.auth() self.auth()
self.stub_catalog_api(data=self.program) self.stub_catalog_api(programs=[self.program])
self.cache_programs()
self.details_page.visit() self.details_page.visit()
......
...@@ -50,12 +50,10 @@ from lms.djangoapps.grades.config.waffle import waffle as grades_waffle, ASSUME_ ...@@ -50,12 +50,10 @@ from lms.djangoapps.grades.config.waffle import waffle as grades_waffle, ASSUME_
from milestones.tests.utils import MilestonesTestCaseMixin from milestones.tests.utils import MilestonesTestCaseMixin
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, CourseRunFactory from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, CourseRunFactory
from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.crawlers.models import CrawlersConfig from openedx.core.djangoapps.crawlers.models import CrawlersConfig
from openedx.core.djangoapps.credit.api import set_credit_requirements from openedx.core.djangoapps.credit.api import set_credit_requirements
from openedx.core.djangoapps.credit.models import CreditCourse, CreditProvider from openedx.core.djangoapps.credit.models import CreditCourse, CreditProvider
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.lib.gating import api as gating_api from openedx.core.lib.gating import api as gating_api
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
...@@ -966,8 +964,10 @@ class ViewsTestCase(ModuleStoreTestCase): ...@@ -966,8 +964,10 @@ class ViewsTestCase(ModuleStoreTestCase):
@attr(shard=2) @attr(shard=2)
@patch('openedx.core.djangoapps.catalog.utils.get_edx_api_data') # Patching 'lms.djangoapps.courseware.views.views.get_programs' would be ideal,
class TestProgramMarketingView(ProgramsApiConfigMixin, CatalogIntegrationMixin, SharedModuleStoreTestCase): # but for some unknown reason that patch doesn't seem to be applied.
@patch('openedx.core.djangoapps.catalog.utils.cache')
class TestProgramMarketingView(SharedModuleStoreTestCase):
"""Unit tests for the program marketing page.""" """Unit tests for the program marketing page."""
program_uuid = str(uuid4()) program_uuid = str(uuid4())
url = reverse('program_marketing_view', kwargs={'program_uuid': program_uuid}) url = reverse('program_marketing_view', kwargs={'program_uuid': program_uuid})
...@@ -982,25 +982,20 @@ class TestProgramMarketingView(ProgramsApiConfigMixin, CatalogIntegrationMixin, ...@@ -982,25 +982,20 @@ class TestProgramMarketingView(ProgramsApiConfigMixin, CatalogIntegrationMixin,
cls.data = ProgramFactory(uuid=cls.program_uuid, courses=[course]) cls.data = ProgramFactory(uuid=cls.program_uuid, courses=[course])
def test_404_if_no_data(self, _mock_get_edx_api_data): def test_404_if_no_data(self, mock_cache):
""" """
Verify that the page 404s if no program data is found. Verify that the page 404s if no program data is found.
""" """
self.create_programs_config() mock_cache.get.return_value = None
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_200(self, mock_get_edx_api_data): def test_200(self, mock_cache):
""" """
Verify the view returns a 200. Verify the view returns a 200.
""" """
self.create_programs_config() mock_cache.get.return_value = self.data
catalog_integration = self.create_catalog_integration()
UserFactory(username=catalog_integration.service_username)
mock_get_edx_api_data.return_value = self.data
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
......
...@@ -228,7 +228,7 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar ...@@ -228,7 +228,7 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar
@skip_unless_lms @skip_unless_lms
@mock.patch(CATALOG_UTILS_MODULE + '.get_edx_api_data') @mock.patch(PROGRAMS_UTILS_MODULE + '.get_programs')
class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, SharedModuleStoreTestCase): class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, SharedModuleStoreTestCase):
"""Unit tests for the program details page.""" """Unit tests for the program details page."""
program_uuid = str(uuid4()) program_uuid = str(uuid4())
...@@ -266,7 +266,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared ...@@ -266,7 +266,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared
any(soup.find_all('a', class_='tab-nav-link', href=reverse('program_listing_view'))) any(soup.find_all('a', class_='tab-nav-link', href=reverse('program_listing_view')))
) )
def test_login_required(self, mock_get_edx_api_data): def test_login_required(self, mock_get_programs):
""" """
Verify that login is required to access the page. Verify that login is required to access the page.
""" """
...@@ -275,7 +275,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared ...@@ -275,7 +275,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared
catalog_integration = self.create_catalog_integration() catalog_integration = self.create_catalog_integration()
UserFactory(username=catalog_integration.service_username) UserFactory(username=catalog_integration.service_username)
mock_get_edx_api_data.return_value = self.data mock_get_programs.return_value = self.data
self.client.logout() self.client.logout()
...@@ -290,7 +290,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared ...@@ -290,7 +290,7 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared
response = self.client.get(self.url) response = self.client.get(self.url)
self.assert_program_data_present(response) self.assert_program_data_present(response)
def test_404_if_disabled(self, _mock_get_edx_api_data): def test_404_if_disabled(self, _mock_get_programs):
""" """
Verify that the page 404s if disabled. Verify that the page 404s if disabled.
""" """
...@@ -299,9 +299,11 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared ...@@ -299,9 +299,11 @@ class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, Shared
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_404_if_no_data(self, _mock_get_edx_api_data): def test_404_if_no_data(self, mock_get_programs):
"""Verify that the page 404s if no program data is found.""" """Verify that the page 404s if no program data is found."""
self.create_programs_config() self.create_programs_config()
mock_get_programs.return_value = None
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
...@@ -84,6 +84,7 @@ ...@@ -84,6 +84,7 @@
"ALLOW_AUTOMATED_SIGNUPS": true, "ALLOW_AUTOMATED_SIGNUPS": true,
"AUTOMATIC_AUTH_FOR_TESTING": true, "AUTOMATIC_AUTH_FOR_TESTING": true,
"MODE_CREATION_FOR_TESTING": true, "MODE_CREATION_FOR_TESTING": true,
"EXPOSE_CACHE_PROGRAMS_ENDPOINT": true,
"AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING": true, "AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING": true,
"ENABLE_COURSE_DISCOVERY": true, "ENABLE_COURSE_DISCOVERY": true,
"ENABLE_SPECIAL_EXAMS": true, "ENABLE_SPECIAL_EXAMS": true,
......
...@@ -290,6 +290,10 @@ FEATURES = { ...@@ -290,6 +290,10 @@ FEATURES = {
# For easily adding modes to courses during acceptance testing # For easily adding modes to courses during acceptance testing
'MODE_CREATION_FOR_TESTING': False, 'MODE_CREATION_FOR_TESTING': False,
# For caching programs in contexts where the LMS can only
# be reached over HTTP.
'EXPOSE_CACHE_PROGRAMS_ENDPOINT': False,
# Courseware search feature # Courseware search feature
'ENABLE_COURSEWARE_SEARCH': False, 'ENABLE_COURSEWARE_SEARCH': False,
......
...@@ -86,6 +86,8 @@ urlpatterns = ( ...@@ -86,6 +86,8 @@ urlpatterns = (
url(r'^rss_proxy/', include('rss_proxy.urls', namespace='rss_proxy')), url(r'^rss_proxy/', include('rss_proxy.urls', namespace='rss_proxy')),
url(r'^api/organizations/', include('organizations.urls', namespace='organizations')), url(r'^api/organizations/', include('organizations.urls', namespace='organizations')),
url(r'^catalog/', include('openedx.core.djangoapps.catalog.urls', namespace='catalog')),
# Update session view # Update session view
url( url(
r'^lang_pref/session_language', r'^lang_pref/session_language',
......
...@@ -7,7 +7,6 @@ import mock ...@@ -7,7 +7,6 @@ import mock
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase from django.test import TestCase
from waffle.models import Switch
from openedx.core.djangoapps.catalog.cache import PROGRAM_CACHE_KEY_TPL, PROGRAM_UUIDS_CACHE_KEY from openedx.core.djangoapps.catalog.cache import PROGRAM_CACHE_KEY_TPL, PROGRAM_UUIDS_CACHE_KEY
from openedx.core.djangoapps.catalog.models import CatalogIntegration from openedx.core.djangoapps.catalog.models import CatalogIntegration
...@@ -29,14 +28,9 @@ User = get_user_model() # pylint: disable=invalid-name ...@@ -29,14 +28,9 @@ User = get_user_model() # pylint: disable=invalid-name
@skip_unless_lms @skip_unless_lms
@mock.patch(UTILS_MODULE + '.logger.warning') @mock.patch(UTILS_MODULE + '.logger.warning')
class TestGetCachedPrograms(CacheIsolationTestCase): class TestGetPrograms(CacheIsolationTestCase):
ENABLED_CACHES = ['default'] ENABLED_CACHES = ['default']
def setUp(self):
super(TestGetCachedPrograms, self).setUp()
Switch.objects.create(name='read_cached_programs', active=True)
def test_get_many(self, mock_warning): def test_get_many(self, mock_warning):
programs = ProgramFactory.create_batch(3) programs = ProgramFactory.create_batch(3)
...@@ -122,130 +116,6 @@ class TestGetCachedPrograms(CacheIsolationTestCase): ...@@ -122,130 +116,6 @@ class TestGetCachedPrograms(CacheIsolationTestCase):
@skip_unless_lms @skip_unless_lms
@mock.patch(UTILS_MODULE + '.get_edx_api_data') @mock.patch(UTILS_MODULE + '.get_edx_api_data')
class TestGetPrograms(CatalogIntegrationMixin, TestCase):
"""Tests covering retrieval of programs from the catalog service."""
def setUp(self):
super(TestGetPrograms, self).setUp()
self.uuid = str(uuid.uuid4())
self.types = ['Foo', 'Bar', 'FooBar']
self.catalog_integration = self.create_catalog_integration(cache_ttl=1)
UserFactory(username=self.catalog_integration.service_username)
def assert_contract(self, call_args, program_uuid=None, types=None):
"""Verify that API data retrieval utility is used correctly."""
args, kwargs = call_args
for arg in (self.catalog_integration, 'programs'):
self.assertIn(arg, args)
self.assertEqual(kwargs['resource_id'], program_uuid)
types_param = ','.join(types) if types and isinstance(types, list) else None
cache_key = '{base}.programs{types}'.format(
base=self.catalog_integration.CACHE_KEY,
types='.' + types_param if types_param else ''
)
self.assertEqual(
kwargs['cache_key'],
cache_key if self.catalog_integration.is_cache_enabled else None
)
self.assertEqual(kwargs['api']._store['base_url'], self.catalog_integration.internal_api_url) # pylint: disable=protected-access
querystring = {
'exclude_utm': 1,
'status': ('active', 'retired',),
}
if program_uuid:
querystring['use_full_course_serializer'] = 1
if types:
querystring['types'] = types_param
self.assertEqual(kwargs['querystring'], querystring)
return args, kwargs
def test_get_programs(self, mock_get_edx_api_data):
programs = ProgramFactory.create_batch(3)
mock_get_edx_api_data.return_value = programs
data = get_programs()
self.assert_contract(mock_get_edx_api_data.call_args)
self.assertEqual(data, programs)
def test_get_one_program(self, mock_get_edx_api_data):
program = ProgramFactory()
mock_get_edx_api_data.return_value = program
data = get_programs(uuid=self.uuid)
self.assert_contract(mock_get_edx_api_data.call_args, program_uuid=self.uuid)
self.assertEqual(data, program)
def test_programs_unavailable(self, mock_get_edx_api_data):
mock_get_edx_api_data.return_value = []
data = get_programs()
self.assert_contract(mock_get_edx_api_data.call_args)
self.assertEqual(data, [])
def test_cache_disabled(self, mock_get_edx_api_data):
self.catalog_integration = self.create_catalog_integration(cache_ttl=0)
get_programs()
self.assert_contract(mock_get_edx_api_data.call_args)
def test_config_missing(self, _mock_get_edx_api_data):
"""
Verify that no errors occur if this method is called when catalog config
is missing.
"""
CatalogIntegration.objects.all().delete()
data = get_programs()
self.assertEqual(data, [])
def test_service_user_missing(self, _mock_get_edx_api_data):
"""
Verify that no errors occur if this method is called when the catalog
service user is missing.
"""
# Note: Deleting the service user would be ideal, but causes mysterious
# errors on Jenkins.
self.create_catalog_integration(service_username='nonexistent-user')
data = get_programs()
self.assertEqual(data, [])
@mock.patch(UTILS_MODULE + '.get_programs')
@mock.patch(UTILS_MODULE + '.get_program_types')
def test_get_programs_with_type(self, mock_get_program_types, mock_get_programs, _mock_get_edx_api_data):
"""Verify get_programs_with_type returns the expected list of programs."""
programs_with_program_type = []
programs = ProgramFactory.create_batch(2)
program_types = []
for program in programs:
program_type = ProgramTypeFactory(name=program['type'])
program_types.append(program_type)
program_with_type = copy.deepcopy(program)
program_with_type['type'] = program_type
programs_with_program_type.append(program_with_type)
mock_get_programs.return_value = programs
mock_get_program_types.return_value = program_types
actual = get_programs_with_type()
self.assertEqual(actual, programs_with_program_type)
@skip_unless_lms
@mock.patch(UTILS_MODULE + '.get_edx_api_data')
class TestGetProgramTypes(CatalogIntegrationMixin, TestCase): class TestGetProgramTypes(CatalogIntegrationMixin, TestCase):
"""Tests covering retrieval of program types from the catalog service.""" """Tests covering retrieval of program types from the catalog service."""
def test_get_program_types(self, mock_get_edx_api_data): def test_get_program_types(self, mock_get_edx_api_data):
......
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^management/cache_programs/$', views.cache_programs, name='cache_programs'),
]
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import copy import copy
import logging import logging
import waffle
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.cache import cache from django.core.cache import cache
...@@ -39,62 +38,27 @@ def get_programs(uuid=None): ...@@ -39,62 +38,27 @@ def get_programs(uuid=None):
list of dict, representing programs. list of dict, representing programs.
dict, if a specific program is requested. dict, if a specific program is requested.
""" """
if waffle.switch_is_active('read_cached_programs'): missing_details_msg_tpl = 'Details for program {uuid} are not cached.'
missing_details_msg_tpl = 'Details for program {uuid} are not cached.'
if uuid: if uuid:
program = cache.get(PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)) program = cache.get(PROGRAM_CACHE_KEY_TPL.format(uuid=uuid))
if not program: if not program:
logger.warning(missing_details_msg_tpl.format(uuid=uuid)) logger.warning(missing_details_msg_tpl.format(uuid=uuid))
return program return program
uuids = cache.get(PROGRAM_UUIDS_CACHE_KEY, []) uuids = cache.get(PROGRAM_UUIDS_CACHE_KEY, [])
if not uuids: if not uuids:
logger.warning('Program UUIDs are not cached.') logger.warning('Program UUIDs are not cached.')
programs = cache.get_many([PROGRAM_CACHE_KEY_TPL.format(uuid=uuid) for uuid in uuids]) programs = cache.get_many([PROGRAM_CACHE_KEY_TPL.format(uuid=uuid) for uuid in uuids])
programs = list(programs.values()) programs = list(programs.values())
missing_uuids = set(uuids) - set(program['uuid'] for program in programs) missing_uuids = set(uuids) - set(program['uuid'] for program in programs)
for uuid in missing_uuids: for uuid in missing_uuids:
logger.warning(missing_details_msg_tpl.format(uuid=uuid)) logger.warning(missing_details_msg_tpl.format(uuid=uuid))
return programs return programs
else:
# Old implementation which may request programs in-process. To be removed
# as part of LEARNER-382.
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}.programs'.format(
base=catalog_integration.CACHE_KEY
)
querystring = {
'exclude_utm': 1,
'status': ('active', 'retired',),
}
if uuid:
querystring['use_full_course_serializer'] = 1
return get_edx_api_data(
catalog_integration,
'programs',
api=api,
resource_id=uuid,
cache_key=cache_key if catalog_integration.is_cache_enabled else None,
querystring=querystring,
)
else:
return []
def get_program_types(name=None): def get_program_types(name=None):
......
from django.conf import settings
from django.core.management import call_command
from django.http import Http404, HttpResponse
from django.views.decorators.http import require_GET
@require_GET
def cache_programs(request):
"""
Call the cache_programs management command.
This view is intended for use in testing contexts where the LMS can only be
reached over HTTP (e.g., Selenium-based browser tests). The discovery service
API the management command attempts to call should be stubbed out first.
"""
if settings.FEATURES.get('EXPOSE_CACHE_PROGRAMS_ENDPOINT'):
call_command('cache_programs')
return HttpResponse('Programs cached.')
raise Http404
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment