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
This commit is contained in:
Renzo Lucioni
2017-05-08 18:50:24 -04:00
parent 278fea9d75
commit 78a9b1f0ae
12 changed files with 134 additions and 218 deletions

View File

@@ -7,7 +7,6 @@ import mock
from django.contrib.auth import get_user_model
from django.core.cache import cache
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.models import CatalogIntegration
@@ -29,14 +28,9 @@ User = get_user_model() # pylint: disable=invalid-name
@skip_unless_lms
@mock.patch(UTILS_MODULE + '.logger.warning')
class TestGetCachedPrograms(CacheIsolationTestCase):
class TestGetPrograms(CacheIsolationTestCase):
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):
programs = ProgramFactory.create_batch(3)
@@ -120,130 +114,6 @@ class TestGetCachedPrograms(CacheIsolationTestCase):
self.assertFalse(mock_warning.called)
@skip_unless_lms
@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):

View File

@@ -0,0 +1,8 @@
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^management/cache_programs/$', views.cache_programs, name='cache_programs'),
]

View File

@@ -2,7 +2,6 @@
import copy
import logging
import waffle
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.cache import cache
@@ -39,62 +38,27 @@ def get_programs(uuid=None):
list of dict, representing programs.
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:
program = cache.get(PROGRAM_CACHE_KEY_TPL.format(uuid=uuid))
if not program:
logger.warning(missing_details_msg_tpl.format(uuid=uuid))
return program
uuids = cache.get(PROGRAM_UUIDS_CACHE_KEY, [])
if not uuids:
logger.warning('Program UUIDs are not cached.')
programs = cache.get_many([PROGRAM_CACHE_KEY_TPL.format(uuid=uuid) for uuid in uuids])
programs = list(programs.values())
missing_uuids = set(uuids) - set(program['uuid'] for program in programs)
for uuid in missing_uuids:
if uuid:
program = cache.get(PROGRAM_CACHE_KEY_TPL.format(uuid=uuid))
if not program:
logger.warning(missing_details_msg_tpl.format(uuid=uuid))
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 []
return program
api = create_catalog_api_client(user, catalog_integration)
uuids = cache.get(PROGRAM_UUIDS_CACHE_KEY, [])
if not uuids:
logger.warning('Program UUIDs are not cached.')
cache_key = '{base}.programs'.format(
base=catalog_integration.CACHE_KEY
)
programs = cache.get_many([PROGRAM_CACHE_KEY_TPL.format(uuid=uuid) for uuid in uuids])
programs = list(programs.values())
querystring = {
'exclude_utm': 1,
'status': ('active', 'retired',),
}
missing_uuids = set(uuids) - set(program['uuid'] for program in programs)
for uuid in missing_uuids:
logger.warning(missing_details_msg_tpl.format(uuid=uuid))
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 []
return programs
def get_program_types(name=None):

View File

@@ -0,0 +1,21 @@
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