From c3c867f103a77bbae13f81d379b4d5fd65acf108 Mon Sep 17 00:00:00 2001 From: Muhammad Faraz Maqsood Date: Thu, 17 Sep 2026 20:30:48 +0500 Subject: [PATCH] fix: make the enrollment v2 schema match with API returns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two places where the published schema disagreed with the actual responses, both of which any generated client hits: The course date fields are null for courses with no dates set, but weren't declared nullable, so clients were told they're always present and blew up parsing null. The enrollment_allowed GET returns a plain list, but @action inherits the viewset's pagination_class, so drf-spectacular published a paginated envelope for it. The endpoint itself is unchanged — only the schema was wrong. --- openedx/core/djangoapps/enrollments/serializers.py | 11 +++++++---- openedx/core/djangoapps/enrollments/v2/views.py | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/openedx/core/djangoapps/enrollments/serializers.py b/openedx/core/djangoapps/enrollments/serializers.py index decd06e218bb..7a0a68d2e93d 100644 --- a/openedx/core/djangoapps/enrollments/serializers.py +++ b/openedx/core/djangoapps/enrollments/serializers.py @@ -38,10 +38,13 @@ class CourseSerializer(serializers.Serializer): # pylint: disable=abstract-meth course_id = serializers.CharField(source="id") course_name = serializers.CharField(source="display_name_with_default") - enrollment_start = serializers.DateTimeField(format=None) - enrollment_end = serializers.DateTimeField(format=None) - course_start = serializers.DateTimeField(source="start", format=None) - course_end = serializers.DateTimeField(source="end", format=None) + # These are null for courses with no dates set, so they must be declared + # nullable — otherwise the published schema tells every generated client + # the fields are always present and parsing null raises. + enrollment_start = serializers.DateTimeField(format=None, allow_null=True) + enrollment_end = serializers.DateTimeField(format=None, allow_null=True) + course_start = serializers.DateTimeField(source="start", format=None, allow_null=True) + course_end = serializers.DateTimeField(source="end", format=None, allow_null=True) invite_only = serializers.BooleanField(source="invitation_only") course_modes = serializers.SerializerMethodField() pacing_type = serializers.SerializerMethodField() diff --git a/openedx/core/djangoapps/enrollments/v2/views.py b/openedx/core/djangoapps/enrollments/v2/views.py index 4b13e247d8f6..f5f192a521f8 100644 --- a/openedx/core/djangoapps/enrollments/v2/views.py +++ b/openedx/core/djangoapps/enrollments/v2/views.py @@ -402,6 +402,10 @@ def unenroll(self, request): url_path="enrollment_allowed", permission_classes=[permissions.IsAdminUser], throttle_classes=[EnrollmentUserThrottle], + # GET returns a plain list. Actions inherit the viewset's + # pagination_class, which would otherwise make drf-spectacular publish + # a paginated envelope this endpoint never returns. + pagination_class=None, ) def allowed(self, request): """Retrieve, create, or delete CourseEnrollmentAllowed records. Admin-only."""