diff --git a/lms/djangoapps/course_home_api/dates/v1/tests/test_views.py b/lms/djangoapps/course_home_api/dates/v1/tests/test_views.py index 4727a399ee..9e96f4ebd2 100644 --- a/lms/djangoapps/course_home_api/dates/v1/tests/test_views.py +++ b/lms/djangoapps/course_home_api/dates/v1/tests/test_views.py @@ -44,6 +44,8 @@ class DatesTabTestViews(BaseCourseHomeTests): self.assertEqual(response.status_code, 200) self.assertFalse(response.data.get('learner_is_full_access')) + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_DATES_TAB.override(active=True) def test_get_unauthenticated_user(self): self.client.logout() response = self.client.get(self.url) diff --git a/lms/djangoapps/course_home_api/dates/v1/views.py b/lms/djangoapps/course_home_api/dates/v1/views.py index 4fd19e5507..ec1e77cf16 100644 --- a/lms/djangoapps/course_home_api/dates/v1/views.py +++ b/lms/djangoapps/course_home_api/dates/v1/views.py @@ -2,12 +2,12 @@ Dates Tab Views """ -from rest_framework import status from rest_framework.generics import RetrieveAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from edx_django_utils import monitoring as monitoring_utils +from django.http.response import Http404 from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser from opaque_keys.edx.keys import CourseKey @@ -74,7 +74,7 @@ class DatesTabView(RetrieveAPIView): course_key = CourseKey.from_string(course_key_string) if not course_home_mfe_dates_tab_is_active(course_key): - return Response(status=status.HTTP_404_NOT_FOUND) + raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) diff --git a/lms/djangoapps/course_home_api/outline/v1/tests/test_views.py b/lms/djangoapps/course_home_api/outline/v1/tests/test_views.py index 7229dc84ab..719ff711fa 100644 --- a/lms/djangoapps/course_home_api/outline/v1/tests/test_views.py +++ b/lms/djangoapps/course_home_api/outline/v1/tests/test_views.py @@ -7,6 +7,7 @@ from django.urls import reverse from course_modes.models import CourseMode from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests +from lms.djangoapps.course_home_api.toggles import COURSE_HOME_MICROFRONTEND, COURSE_HOME_MICROFRONTEND_OUTLINE_TAB from openedx.core.djangoapps.user_api.preferences.api import set_user_preference from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactory from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG @@ -25,6 +26,8 @@ class OutlineTabTestViews(BaseCourseHomeTests): super().setUp() self.url = reverse('course-home-outline-tab', args=[self.course.id]) + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True) @ddt.data(CourseMode.AUDIT, CourseMode.VERIFIED) def test_get_authenticated_enrolled_user(self, enrollment_mode): CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode) @@ -41,6 +44,8 @@ class OutlineTabTestViews(BaseCourseHomeTests): self.assertTrue(all((block.get('title') != "") for block in date_blocks)) self.assertTrue(all(block.get('date') for block in date_blocks)) + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True) def test_get_authenticated_user_not_enrolled(self): response = self.client.get(self.url) self.assertEqual(response.status_code, 200) @@ -54,11 +59,15 @@ class OutlineTabTestViews(BaseCourseHomeTests): self.assertTrue(all((block.get('title') != "") for block in date_blocks)) self.assertTrue(all(block.get('date') for block in date_blocks)) + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True) def test_get_unauthenticated_user(self): self.client.logout() response = self.client.get(self.url) self.assertEqual(response.status_code, 403) + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True) def test_masquerade(self): user = UserFactory() set_user_preference(user, 'time_zone', 'Asia/Tokyo') @@ -73,6 +82,8 @@ class OutlineTabTestViews(BaseCourseHomeTests): self.update_masquerade(username=user.username) self.assertEqual(self.client.get(self.url).data['dates_widget']['user_timezone'], 'Asia/Tokyo') + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True) @ddt.data( (True, True, True, True), # happy path (True, False, False, True), # is enrolled @@ -97,8 +108,23 @@ class OutlineTabTestViews(BaseCourseHomeTests): handouts_html = self.client.get(self.url).data['handouts_html'] self.assertEqual(handouts_html, '

Hi

' if handouts_visible else '') - # TODO: write test_get_unknown_course when more data is pulled into the Outline Tab API + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True) + def test_get_unknown_course(self): + url = reverse('course-home-outline-tab', args=['course-v1:unknown+course+2T2020']) + response = self.client.get(url) + self.assertEqual(response.status_code, 404) + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=False) + @ddt.data(CourseMode.AUDIT, CourseMode.VERIFIED) + def test_waffle_flag_disabled(self, enrollment_mode): + CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode) + response = self.client.get(self.url) + self.assertEqual(response.status_code, 404) + + @COURSE_HOME_MICROFRONTEND.override(active=True) + @COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True) @ddt.data(True, False) def test_welcome_message(self, welcome_message_is_dismissed): self.store.create_item( diff --git a/lms/djangoapps/course_home_api/outline/v1/views.py b/lms/djangoapps/course_home_api/outline/v1/views.py index 3f4ae49c59..911a546a7b 100644 --- a/lms/djangoapps/course_home_api/outline/v1/views.py +++ b/lms/djangoapps/course_home_api/outline/v1/views.py @@ -11,13 +11,14 @@ from rest_framework.response import Response from django.utils.translation import ugettext as _ from edx_django_utils import monitoring as monitoring_utils from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication +from django.http.response import Http404 from django.urls import reverse from opaque_keys.edx.keys import CourseKey from lms.djangoapps.course_api.blocks.transformers.blocks_api import BlocksAPITransformer from lms.djangoapps.course_blocks.api import get_course_block_access_transformers, get_course_blocks from lms.djangoapps.course_home_api.outline.v1.serializers import OutlineTabSerializer -from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active +from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active, course_home_mfe_outline_tab_is_active from lms.djangoapps.course_home_api.utils import get_microfrontend_url from lms.djangoapps.courseware.access import has_access from lms.djangoapps.courseware.context_processor import user_timezone_locale_prefs @@ -89,6 +90,9 @@ class OutlineTabView(RetrieveAPIView): course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) + if not course_home_mfe_outline_tab_is_active(course_key): + raise Http404 + # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) diff --git a/lms/djangoapps/course_home_api/toggles.py b/lms/djangoapps/course_home_api/toggles.py index bc486be81f..b985db0782 100644 --- a/lms/djangoapps/course_home_api/toggles.py +++ b/lms/djangoapps/course_home_api/toggles.py @@ -11,9 +11,18 @@ COURSE_HOME_MICROFRONTEND = ExperimentWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'course_ COURSE_HOME_MICROFRONTEND_DATES_TAB = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'course_home_mfe_dates_tab') +COURSE_HOME_MICROFRONTEND_OUTLINE_TAB = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'course_home_mfe_outline_tab') + def course_home_mfe_dates_tab_is_active(course_key): return ( COURSE_HOME_MICROFRONTEND.is_enabled(course_key) and COURSE_HOME_MICROFRONTEND_DATES_TAB.is_enabled(course_key) ) + + +def course_home_mfe_outline_tab_is_active(course_key): + return ( + COURSE_HOME_MICROFRONTEND.is_enabled(course_key) and + COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.is_enabled(course_key) + ) diff --git a/lms/djangoapps/courseware/tabs.py b/lms/djangoapps/courseware/tabs.py index 57962266b5..0971b2fefa 100644 --- a/lms/djangoapps/courseware/tabs.py +++ b/lms/djangoapps/courseware/tabs.py @@ -11,7 +11,7 @@ from django.utils.translation import ugettext_noop from lms.djangoapps.courseware.access import has_access from lms.djangoapps.courseware.entrance_exams import user_can_skip_entrance_exam -from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active +from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active, course_home_mfe_outline_tab_is_active from lms.djangoapps.course_home_api.utils import get_microfrontend_url from openedx.core.lib.course_tabs import CourseTabPluginManager from openedx.features.course_experience import RELATIVE_DATES_FLAG, UNIFIED_COURSE_TAB_FLAG, default_course_url_name @@ -41,6 +41,18 @@ class CoursewareTab(EnrolledTab): is_default = False supports_preview_menu = True + def __init__(self, tab_dict): + def link_func(course, reverse_func): + if course_home_mfe_outline_tab_is_active(course.id): + return get_microfrontend_url(course_key=course.id, view_name=self.view_name) + else: + reverse_name_func = lambda course_test: default_course_url_name(course_test.id) + url_func = course_reverse_func_from_name_func(reverse_name_func) + return url_func(course, reverse_func) + + tab_dict['link_func'] = link_func + super().__init__(tab_dict) + @classmethod def is_enabled(cls, course, user=None): """ @@ -51,15 +63,6 @@ class CoursewareTab(EnrolledTab): return True return super(CoursewareTab, cls).is_enabled(course, user) - @property - def link_func(self): - """ - Returns a function that takes a course and reverse function and will - compute the course URL for this tab. - """ - reverse_name_func = lambda course: default_course_url_name(course.id) - return course_reverse_func_from_name_func(reverse_name_func) - class CourseInfoTab(CourseTab): """