Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b116330
feat: Standardize HomePageCoursesView (#38366)
taimoor-ahmed-1 Jun 2, 2026
164c5df
revert: Revert "[FC-0018] feat: Standardize HomePageCoursesView (#383…
taimoor-ahmed-1 Jun 2, 2026
71af38e
feat: standardize home v2 API into v4 (#38684)
Faraz32123 Jun 3, 2026
1c02335
feat: apply ADRs to home v1 apis & standardize them to v3 (#38694)
taimoor-ahmed-1 Jun 3, 2026
c36faba
feat: standardize xblock API, add v1 (#38723)
Faraz32123 Jun 10, 2026
3db5082
feat: apply ADRs standardization to enrollment apis (#38724)
taimoor-ahmed-1 Jun 10, 2026
f4f9678
feat: Update course detail api version with ADR standardization (#38708)
taimoor-ahmed-1 Jun 10, 2026
2454b50
feat: apply ADR standardization to AuthorGrading apis (#38726)
taimoor-ahmed-1 Jun 15, 2026
2a870a4
feat: apply ADR 0036 (nested JSON normalization) across 6 standardize…
taimoor-ahmed-1 Jun 23, 2026
080544c
feat: apply ADR 0034 (auth standardization) across 6 standardized API…
taimoor-ahmed-1 Jun 23, 2026
427bb8e
feat: add openai schema tag for our versioned APIs
Faraz32123 Jun 24, 2026
3d7b57d
fix: home API schema after testing with SDK
Faraz32123 Jun 24, 2026
b1c8acb
fix: add serializer removed during rebase
Faraz32123 Jun 24, 2026
f3cabe0
fix: test as v1 home/courses endpoint was removed
Faraz32123 Jun 24, 2026
8ec46fe
feat(adr-0027): add @extend_schema to Xblock v1 viewset (#38834)
taimoor-ahmed-1 Jul 2, 2026
794c440
feat: fix OpenAPI schemas for SDK compatibility (#38845)
Faraz32123 Jul 3, 2026
5c3fbec
feat: configure drf-spectacular on LMS for enrollment API schema gene…
Faraz32123 Jul 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cms/djangoapps/contentstore/rest_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
from .v0 import urls as v0_urls
from .v1 import urls as v1_urls
from .v2 import urls as v2_urls
from .v3 import urls as v3_urls
from .v4 import urls as v4_urls

app_name = 'cms.djangoapps.contentstore'

urlpatterns = [
path('v0/', include(v0_urls)),
path('v1/', include(v1_urls)),
path('v2/', include(v2_urls))
path('v2/', include(v2_urls)),
path('v3/', include(v3_urls)),
path('v4/', include(v4_urls)),
]
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,37 @@ class Meta:
ref_name = "authoring_grading.Graders.v0"


class GracePeriodSerializer(serializers.Serializer):
"""Serializer for grace period (hours / minutes / seconds)."""
hours = serializers.IntegerField(default=0)
minutes = serializers.IntegerField(default=0)
seconds = serializers.IntegerField(default=0, required=False)

class Meta:
ref_name = "authoring_grading.GracePeriod.v0"


class CourseGradingModelSerializer(serializers.Serializer):
""" Serializer for course grading model data """
graders = GradersSerializer(many=True, allow_null=True, allow_empty=True)
grade_cutoffs = serializers.DictField(
child=serializers.FloatField(),
required=False,
help_text=(
"Mapping of letter grade to minimum score (0.0–1.0). "
"Required by CourseGradingModel.update_from_json — must be included in every PATCH."
),
)
grace_period = GracePeriodSerializer(
allow_null=True,
required=False,
help_text="Grace period duration. Pass null to clear the grace period.",
)
minimum_grade_credit = serializers.FloatField(
required=False,
allow_null=True,
help_text="Minimum passing score for credit eligibility (0.0–1.0).",
)

class Meta:
ref_name = "authoring_grading.CourseGrading.v0"
23 changes: 22 additions & 1 deletion cms/djangoapps/contentstore/rest_api/v0/views/xblock.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
"""
Public rest API endpoints for the CMS API.
Public rest API endpoints for the CMS API — v0 xblock (DEPRECATED).

.. deprecated::
These views are superseded by ``XblockViewSet`` in
``cms.djangoapps.contentstore.rest_api.v1.views.xblock``.
Use ``/api/contentstore/v1/xblock/`` going forward.
These v0 endpoints will be removed in a future release.
"""
import logging
import warnings

from django.views.decorators.csrf import csrf_exempt
from rest_framework.generics import CreateAPIView, RetrieveUpdateDestroyAPIView
Expand All @@ -17,10 +24,17 @@
log = logging.getLogger(__name__)
handle_xblock = view_handlers.handle_xblock

_DEPRECATION_MSG = (
"The v0 xblock API (/api/contentstore/v0/xblock/) is deprecated. "
"Use /api/contentstore/v1/xblock/ instead."
)


@view_auth_classes()
class XblockView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView):
"""
**DEPRECATED** — use ``/api/contentstore/v1/xblock/{usage_key_string}/`` instead.

Public rest API endpoints for the CMS API.
course_key: required argument, needed to authorize course authors.
usage_key_string (optional):
Expand All @@ -32,29 +46,35 @@ class XblockView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView):
@course_author_access_required
@expect_json_in_class_view
def retrieve(self, request, course_key, usage_key_string=None):
warnings.warn(_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
return handle_xblock(request, usage_key_string)

@course_author_access_required
@expect_json_in_class_view
@validate_request_with_serializer
def update(self, request, course_key, usage_key_string=None):
warnings.warn(_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
return handle_xblock(request, usage_key_string)

@course_author_access_required
@expect_json_in_class_view
@validate_request_with_serializer
def partial_update(self, request, course_key, usage_key_string=None):
warnings.warn(_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
return handle_xblock(request, usage_key_string)

@course_author_access_required
@expect_json_in_class_view
def destroy(self, request, course_key, usage_key_string=None):
warnings.warn(_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
return handle_xblock(request, usage_key_string)


@view_auth_classes()
class XblockCreateView(DeveloperErrorViewMixin, CreateAPIView):
"""
**DEPRECATED** — use ``POST /api/contentstore/v1/xblock/`` instead.

Public rest API endpoints for the CMS API.
course_key: required argument, needed to authorize course authors.
usage_key_string (optional):
Expand All @@ -68,4 +88,5 @@ class XblockCreateView(DeveloperErrorViewMixin, CreateAPIView):
@expect_json_in_class_view
@validate_request_with_serializer
def create(self, request, course_key, usage_key_string=None):
warnings.warn(_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
return handle_xblock(request, usage_key_string)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .course_waffle_flags import CourseWaffleFlagsSerializer # noqa: F401
from .grading import CourseGradingModelSerializer, CourseGradingSerializer # noqa: F401
from .group_configurations import CourseGroupConfigurationsSerializer # noqa: F401
from .home import LibraryTabSerializer, StudioHomeSerializer # noqa: F401
from .home import CourseHomeTabSerializer, LibraryTabSerializer, StudioHomeSerializer # noqa: F401
from .proctoring import (
LimitedProctoredExamSettingsSerializer, # noqa: F401
ProctoredExamConfigurationSerializer, # noqa: F401
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class CourseDetailsSerializer(serializers.Serializer):
about_sidebar_html = serializers.CharField(allow_null=True, allow_blank=True)
banner_image_name = serializers.CharField(allow_blank=True)
banner_image_asset_path = serializers.CharField()
certificate_available_date = serializers.DateTimeField()
certificate_available_date = serializers.DateTimeField(allow_null=True)
certificates_display_behavior = serializers.CharField(allow_null=True)
course_id = serializers.CharField()
course_image_asset_path = serializers.CharField(allow_blank=True)
Expand Down
7 changes: 7 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/serializers/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ class LibraryTabSerializer(serializers.Serializer):
libraries = LibraryViewSerializer(many=True, required=False, allow_null=True)


class CourseHomeTabSerializer(serializers.Serializer):
"""Serializer for the courses tab of the Studio home page."""
courses = CourseCommonSerializer(required=False, many=True)
archived_courses = CourseCommonSerializer(required=False, many=True)
in_process_course_actions = UnsucceededCourseSerializer(many=True, required=False, allow_null=True)


class StudioHomeSerializer(serializers.Serializer):
"""Serializer for Studio home"""
allow_course_reruns = serializers.BooleanField()
Expand Down
7 changes: 6 additions & 1 deletion cms/djangoapps/contentstore/rest_api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.conf import settings
from django.urls import path, re_path
from rest_framework.routers import DefaultRouter

from openedx.core.constants import COURSE_ID_PATTERN

Expand All @@ -26,14 +27,18 @@
ProctoringErrorsView,
VideoDownloadView,
VideoUsageView,
XblockViewSet,
vertical_container_children_redirect_view,
)

app_name = 'v1'

VIDEO_ID_PATTERN = r'(?P<edx_video_id>[-\w]+)'

urlpatterns = [
_router = DefaultRouter()
_router.register(r'xblock', XblockViewSet, basename='xblock')

urlpatterns = _router.urls + [
path(
'home',
HomePageView.as_view(),
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 @@ -16,3 +16,4 @@
from .textbooks import CourseTextbooksView # noqa: F401
from .vertical_block import ContainerHandlerView, vertical_container_children_redirect_view # noqa: F401
from .videos import CourseVideosView, VideoDownloadView, VideoUsageView # noqa: F401
from .xblock import XblockViewSet # noqa: F401
27 changes: 27 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/views/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Permission classes for v1 contentstore API views (ADR 0026).
"""
import logging

from rest_framework.permissions import BasePermission

from common.djangoapps.student.auth import has_course_author_access

log = logging.getLogger(__name__)


class HasCourseAuthorAccess(BasePermission):
"""
ADR 0026: replaces the @course_author_access_required decorator.

Reads ``view.kwargs["course_key"]`` (a CourseKey instance) that is
injected by XblockViewSet.initial() before DRF runs permission checks.
Returns 403 if the authenticated user lacks authoring rights on that
course, or if no course key could be derived.
"""

def has_permission(self, request, view):
course_key = getattr(view, "course_key", None)
if not course_key:
return False
return has_course_author_access(request.user, course_key)
Loading
Loading