From 7927213c264b8d236d2fc44ce075f3b9d249eb00 Mon Sep 17 00:00:00 2001 From: KEVYN SUAREZ <91025555+efortish@users.noreply.github.com> Date: Fri, 11 Jul 2025 08:25:30 -0500 Subject: [PATCH] fix: validation and error display for due date extensions in the API (#36187) --- lms/djangoapps/instructor/tests/test_api.py | 30 +++++++++++++++++++++ lms/djangoapps/instructor/views/api.py | 18 ++++++++----- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index ca46872a56..e943701cab 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4336,6 +4336,36 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): assert response.status_code == 400, response.content assert get_extended_due(self.course, self.week3, self.user1) is None + def test_change_to_invalid_username(self): + url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)}) + response = self.client.post(url, { + 'student': 'invalid_username', + 'url': str(self.week1.location), + 'due_datetime': '12/30/2026 02:00' + }) + assert response.status_code == 404, response.content + assert get_extended_due(self.course, self.week1, self.user1) is None + + def test_change_to_invalid_due_date_format(self): + url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)}) + response = self.client.post(url, { + 'student': self.user1.username, + 'url': str(self.week1.location), + 'due_datetime': '12/30/2kkk 00:00:00' + }) + assert response.status_code == 400, response.content + assert get_extended_due(self.course, self.week1, self.user1) is None + + def test_change_with_blank_fields(self): + url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)}) + response = self.client.post(url, { + 'student': '', + 'url': '', + 'due_datetime': '' + }) + assert response.status_code == 400, response.content + assert get_extended_due(self.course, self.week1, self.user1) is None + @override_waffle_flag(RELATIVE_DATES_FLAG, active=True) def test_reset_date(self): self.test_change_due_date() diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 43e7293ce6..4a666727e2 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -3121,24 +3121,30 @@ class ChangeDueDate(APIView): """ serializer_data = self.serializer_class(data=request.data) if not serializer_data.is_valid(): - return HttpResponseBadRequest(reason=serializer_data.errors) + return JsonResponseBadRequest({'error': _('All fields must be filled out')}) student = serializer_data.validated_data.get('student') if not student: response_payload = { - 'error': f'Could not find student matching identifier: {request.data.get("student")}' + 'error': _( + 'Could not find student matching identifier: {student}' + ).format(student=request.data.get("student")) } - return JsonResponse(response_payload) + return JsonResponse(response_payload, status=status.HTTP_404_NOT_FOUND) + + due_datetime = serializer_data.validated_data.get('due_datetime') + try: + due_date = parse_datetime(due_datetime) + except DashboardError: + return JsonResponseBadRequest({'error': _('The extension due date and time format is incorrect')}) course = get_course_by_id(CourseKey.from_string(course_id)) - unit = find_unit(course, serializer_data.validated_data.get('url')) - due_date = parse_datetime(serializer_data.validated_data.get('due_datetime')) reason = strip_tags(serializer_data.validated_data.get('reason', '')) try: set_due_date_extension(course, unit, student, due_date, request.user, reason=reason) except Exception as error: # pylint: disable=broad-except - return JsonResponse({'error': str(error)}, status=400) + return JsonResponseBadRequest({'error': str(error)}) return JsonResponse(_( 'Successfully changed due date for student {0} for {1} '