Skip to content

Commit

Permalink
feat: [FC-0070] [BE] Remove backend redirects (use SPA functionality)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sagirov Eugeniy committed Oct 31, 2024
1 parent b20498c commit 497ef43
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .course_index import CourseIndexSerializer
from .course_rerun import CourseRerunSerializer
from .course_team import CourseTeamSerializer
from .course_waffle_flags import CourseWaffleFlagsSerializer
from .grading import CourseGradingModelSerializer, CourseGradingSerializer
from .group_configurations import CourseGroupConfigurationsSerializer
from .home import StudioHomeSerializer, CourseHomeTabSerializer, LibraryTabSerializer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"""
API Serializers for course waffle flags
"""

from rest_framework import serializers

from cms.djangoapps.contentstore import toggles


class CourseWaffleFlagsSerializer(serializers.Serializer):
"""
Serializer for course waffle flags
"""
use_new_home_page = serializers.SerializerMethodField()
use_new_custom_pages = serializers.SerializerMethodField()
use_new_schedule_details_page = serializers.SerializerMethodField()
use_new_advanced_settings_page = serializers.SerializerMethodField()
use_new_grading_page = serializers.SerializerMethodField()
use_new_updates_page = serializers.SerializerMethodField()
use_new_import_page = serializers.SerializerMethodField()
use_new_export_page = serializers.SerializerMethodField()
use_new_files_uploads_page = serializers.SerializerMethodField()
use_new_video_uploads_page = serializers.SerializerMethodField()
use_new_course_outline_page = serializers.SerializerMethodField()
use_new_unit_page = serializers.SerializerMethodField()
use_new_course_team_page = serializers.SerializerMethodField()
use_new_certificates_page = serializers.SerializerMethodField()
use_new_textbooks_page = serializers.SerializerMethodField()
use_new_group_configurations_page = serializers.SerializerMethodField()

def get_course_key(self):
"""
Retrieve the course_key from the context
"""
return self.context.get("course_key")

def get_use_new_home_page(self, obj):
"""
Method to get the use_new_home_page switch
"""
return toggles.use_new_home_page()

def get_use_new_custom_pages(self, obj):
"""
Method to get the use_new_custom_pages switch
"""
course_key = self.get_course_key()
return toggles.use_new_custom_pages(course_key)

def get_use_new_schedule_details_page(self, obj):
"""
Method to get the use_new_schedule_details_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_schedule_details_page(course_key)

def get_use_new_advanced_settings_page(self, obj):
"""
Method to get the use_new_advanced_settings_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_advanced_settings_page(course_key)

def get_use_new_grading_page(self, obj):
"""
Method to get the use_new_grading_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_grading_page(course_key)

def get_use_new_updates_page(self, obj):
"""
Method to get the use_new_updates_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_updates_page(course_key)

def get_use_new_import_page(self, obj):
"""
Method to get the use_new_import_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_import_page(course_key)

def get_use_new_export_page(self, obj):
"""
Method to get the use_new_export_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_export_page(course_key)

def get_use_new_files_uploads_page(self, obj):
"""
Method to get the use_new_files_uploads_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_files_uploads_page(course_key)

def get_use_new_video_uploads_page(self, obj):
"""
Method to get the use_new_video_uploads_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_video_uploads_page(course_key)

def get_use_new_course_outline_page(self, obj):
"""
Method to get the use_new_course_outline_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_course_outline_page(course_key)

def get_use_new_unit_page(self, obj):
"""
Method to get the use_new_unit_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_unit_page(course_key)

def get_use_new_course_team_page(self, obj):
"""
Method to get the use_new_course_team_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_course_team_page(course_key)

def get_use_new_certificates_page(self, obj):
"""
Method to get the use_new_certificates_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_certificates_page(course_key)

def get_use_new_textbooks_page(self, obj):
"""
Method to get the use_new_textbooks_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_textbooks_page(course_key)

def get_use_new_group_configurations_page(self, obj):
"""
Method to get the use_new_group_configurations_page switch
"""
course_key = self.get_course_key()
return toggles.use_new_group_configurations_page(course_key)
6 changes: 6 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
CourseRerunView,
CourseSettingsView,
CourseVideosView,
CourseWaffleFlagsView,
HomePageView,
HomePageCoursesView,
HomePageLibrariesView,
Expand Down Expand Up @@ -131,6 +132,11 @@
VerticalContainerView.as_view(),
name="container_vertical"
),
re_path(
fr'^course_waffle_flags(?:/{COURSE_ID_PATTERN})?$',
CourseWaffleFlagsView.as_view(),
name="course_waffle_flags"
),

# Authoring API
# Do not use under v1 yet (Nov. 23). The Authoring API is still experimental and the v0 versions should be used
Expand Down
1 change: 1 addition & 0 deletions cms/djangoapps/contentstore/rest_api/v1/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .course_details import CourseDetailsView
from .course_index import CourseIndexView
from .course_rerun import CourseRerunView
from .course_waffle_flags import CourseWaffleFlagsView
from .course_team import CourseTeamView
from .grading import CourseGradingView
from .group_configurations import CourseGroupConfigurationsView
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
""" API Views for course waffle flags """

from opaque_keys.edx.keys import CourseKey
from rest_framework.decorators import APIView
from rest_framework.response import Response

from openedx.core.lib.api.view_utils import view_auth_classes

from ..serializers import CourseWaffleFlagsSerializer


@view_auth_classes(is_authenticated=True)
class CourseWaffleFlagsView(APIView):
"""
API view to retrieve course waffle flag settings for a specific course.
This view provides a GET endpoint that returns the status of various waffle
flags for a given course. It requires the user to be authenticated.
"""

def get(self, request, course_id=None):
"""
Retrieve the waffle flag settings for the specified course.
Args:
request (HttpRequest): The HTTP request object.
course_id (str, optional): The ID of the course for which to retrieve
the waffle flag settings. If not provided,
defaults to None.
Returns:
Response: A JSON response containing the status of various waffle flags
for the specified course.
**Example Request**
GET /api/contentstore/v1/course_waffle_flags
GET /api/contentstore/v1/course_waffle_flags/course-v1:test+test+test
**Response Values**
A JSON response containing the status of various waffle flags
for the specified course.
**Example Response**
```json
{
"use_new_home_page": true,
"use_new_custom_pages": true,
"use_new_schedule_details_page": true,
"use_new_advanced_settings_page": true,
"use_new_grading_page": true,
"use_new_updates_page": true,
"use_new_import_page": true,
"use_new_export_page": true,
"use_new_files_uploads_page": true,
"use_new_video_uploads_page": false,
"use_new_course_outline_page": true,
"use_new_unit_page": false,
"use_new_course_team_page": true,
"use_new_certificates_page": true,
"use_new_textbooks_page": true,
"use_new_group_configurations_page": true
}
```
"""
course_key = CourseKey.from_string(course_id) if course_id else None
serializer = CourseWaffleFlagsSerializer(
context={"course_key": course_key}, data={}
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data)
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
Unit tests for the CourseWaffleFlagsView endpoint in the Open edX platform.
These tests validate the behavior of waffle flags for specific courses or default
global states when no course ID is provided.
"""

from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status

from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel

User = get_user_model()


class CourseWaffleFlagsViewTest(CourseTestCase):
"""
Tests for the CourseWaffleFlagsView endpoint, which returns waffle flag states
for a specific course or globally if no course ID is provided.
"""

course_waffle_flags = [
"use_new_custom_pages",
"use_new_schedule_details_page",
"use_new_advanced_settings_page",
"use_new_grading_page",
"use_new_updates_page",
"use_new_import_page",
"use_new_export_page",
"use_new_files_uploads_page",
"use_new_video_uploads_page",
"use_new_course_outline_page",
"use_new_unit_page",
"use_new_course_team_page",
"use_new_certificates_page",
"use_new_textbooks_page",
"use_new_group_configurations_page",
]

def setUp(self):
"""
Set up test data and state before each test method.
This method initializes the endpoint URL and creates a set of waffle flags
for the test course, setting each flag's value to `True`.
"""
super().setUp()
self.url = reverse("cms.djangoapps.contentstore:v1:course_waffle_flags")
self.create_waffle_flags(self.course_waffle_flags)

def create_waffle_flags(self, flags, enabled=True):
"""
Helper method to create waffle flag entries in the database for the test course.
Args:
flags (list): A list of flag names to set up.
enabled (bool): The value to set for each flag's enabled state.
"""
for flag in flags:
WaffleFlagCourseOverrideModel.objects.create(
waffle_flag=f"contentstore.new_studio_mfe.{flag}",
course_id=self.course.id,
enabled=enabled,
)

def expected_response(self, enabled=False):
"""
Generate an expected response dictionary based on the enabled flag.
Args:
enabled (bool): State to assign to each waffle flag in the response.
Returns:
dict: A dictionary with each flag set to the value of `enabled`.
"""
return {flag: enabled for flag in self.course_waffle_flags}

def test_get_course_waffle_flags_with_course_id(self):
"""
Test that waffle flags for a specific course are correctly returned when
a valid course ID is provided.
Expected Behavior:
- The response should return HTTP 200 status.
- Each flag returned should be `True` as set up in the `setUp` method.
"""
course_url = reverse(
"cms.djangoapps.contentstore:v1:course_waffle_flags",
kwargs={"course_id": self.course.id},
)

expected_response = self.expected_response(enabled=True)
expected_response["use_new_home_page"] = False # Assuming this is the default

response = self.client.get(course_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)

def test_get_course_waffle_flags_without_course_id(self):
"""
Test that the default waffle flag states are returned when no course ID is provided.
Expected Behavior:
- The response should return HTTP 200 status.
- Each flag returned should default to `False`, representing the global
default state for each flag.
"""
expected_response = self.expected_response(enabled=False)
expected_response["use_new_home_page"] = False # Assuming this is the default

response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)

0 comments on commit 497ef43

Please sign in to comment.