From 5ef9c0ffccaebc931696ed7415fc27b92f145ba6 Mon Sep 17 00:00:00 2001 From: ruzniaievdm Date: Thu, 15 Feb 2024 18:32:00 +0200 Subject: [PATCH] feat: [AXIMST-490] display render errors (#2506) * feat: [AXIMST-490] display render errors * fix: resolve discussions --- .../rest_api/v1/serializers/vertical_block.py | 6 +-- .../v1/views/tests/test_vertical_block.py | 21 +++----- .../rest_api/v1/views/vertical_block.py | 20 +++---- cms/djangoapps/contentstore/utils.py | 53 ++++++++++++++++--- 4 files changed, 68 insertions(+), 32 deletions(-) diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py index 6d6bbea5d1fc..3ea9c0b0461c 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py +++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py @@ -17,7 +17,7 @@ class MessageValidation(serializers.Serializer): Serializer for representing XBlock error. """ -text = serializers.CharField() + text = serializers.CharField() type = serializers.CharField() @@ -114,8 +114,8 @@ class ChildVerticalContainerSerializer(serializers.Serializer): user_partition_info = serializers.DictField() user_partitions = serializers.ListField() actions = serializers.SerializerMethodField() - has_validation_error = serializers.BooleanField() - validation_errors = MessageValidation(many=True) + validation_messages = MessageValidation(many=True) + render_error = serializers.CharField() def get_actions(self, obj): # pylint: disable=unused-argument """ diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py index c3c3789206bc..6393f83897f7 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py @@ -206,8 +206,8 @@ def test_children_content(self): }, "user_partition_info": expected_user_partition_info, "user_partitions": expected_user_partitions, - "has_validation_error": False, - "validation_errors": [], + "validation_messages": [], + "render_error": "", }, { "name": self.html_unit_second.display_name_with_default, @@ -226,8 +226,8 @@ def test_children_content(self): }, "user_partition_info": expected_user_partition_info, "user_partitions": expected_user_partitions, - "has_validation_error": False, - "validation_errors": [], + "validation_messages": [], + "render_error": "", }, ] self.assertEqual(response.data["children"], expected_response) @@ -286,13 +286,8 @@ def test_validation_errors(self): response = self.client.get(url) children_response = response.data["children"] - # Check for an error in html_unit_first xblock - self.assertTrue(children_response[0]["has_validation_error"]) + # Verify that html_unit_first access settings contradict its parent's access settings. + self.assertEqual(children_response[0]["validation_messages"][0]["type"], ValidationMessage.ERROR) - # Verify that html access settings contradict its parent's access settings. - validation = html_unit_first.validate() - self.assertEqual(len(validation.messages), 1) - self.assertEqual(validation.to_json()['messages'][0]['type'], ValidationMessage.ERROR) - - # Check for an error in html_unit_second xblock - self.assertFalse(children_response[1]["has_validation_error"]) + # Verify that html_unit_second has no validation messages. + self.assertFalse(children_response[1]["validation_messages"]) diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py b/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py index 885d6c06d16e..4a685d822a62 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py @@ -10,7 +10,8 @@ get_container_handler_context, get_user_partition_info, get_visibility_partition_info, - get_validation_messages, + get_xblock_validation_messages, + get_xblock_render_error, ) from cms.djangoapps.contentstore.views.component import _get_item_in_course from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import get_xblock @@ -216,8 +217,8 @@ def get(self, request: Request, usage_key_string: str): "can_delete": true, "can_manage_tags": true, } - "has_validation_error": false, - "validation_errors": [], + "validation_messages": [], + "render_error": "", }, { "name": "Text", @@ -233,13 +234,13 @@ def get(self, request: Request, usage_key_string: str): "can_delete": true "can_manage_tags": true, }, - "has_validation_error": true, - "validation_errors": [ + "validation_messages": [ { "text": "This component's access settings contradict its parent's access settings.", "type": "error" } - ] + ], + "render_error": "Unterminated control keyword: 'if' in file '../problem.html'", }, ], "is_published": false, @@ -258,7 +259,8 @@ def get(self, request: Request, usage_key_string: str): child_info = modulestore().get_item(child) user_partition_info = get_visibility_partition_info(child_info, course=course) user_partitions = get_user_partition_info(child_info, course=course) - validation_errors, has_validation_error = get_validation_messages(child_info) + validation_messages = get_xblock_validation_messages(child_info) + render_error = get_xblock_render_error(request, child_info) children.append({ "name": child_info.display_name_with_default, @@ -266,8 +268,8 @@ def get(self, request: Request, usage_key_string: str): "block_type": child_info.location.block_type, "user_partition_info": user_partition_info, "user_partitions": user_partitions, - "has_validation_error": has_validation_error, - "validation_errors": validation_errors, + "validation_messages": validation_messages, + "render_error": render_error, }) is_published = not modulestore().has_changes(current_xblock) diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 0964be7bc769..ba9183ecaac3 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -2236,7 +2236,7 @@ def send_course_update_notification(course_key, content, user): COURSE_NOTIFICATION_REQUESTED.send_event(course_notification_data=notification_data) -def get_validation_messages(xblock): +def get_xblock_validation_messages(xblock): """ Retrieves validation messages for a given xblock. @@ -2244,11 +2244,50 @@ def get_validation_messages(xblock): xblock: The xblock object to validate. Returns: - tuple: - - validation_errors (list): A list of validation error messages. - - has_validation_error (bool): True if there are validation errors, False otherwise. + list: A list of validation error messages. """ validation_json = xblock.validate().to_json() - validation_errors = validation_json['messages'] - has_validation_error = bool(validation_errors) - return validation_errors, has_validation_error + return validation_json['messages'] + + +def get_xblock_render_error(request, xblock): + """ + Checks if there are any rendering errors for a given block and return these. + + Args: + request: WSGI request object + xblock: The xblock object to rendering. + + Returns: + str: Error message which happened while rendering of xblock. + """ + from cms.djangoapps.contentstore.views.preview import _load_preview_block + from xmodule.studio_editable import has_author_view + from xmodule.x_module import AUTHOR_VIEW, STUDENT_VIEW + + def get_xblock_render_context(request, block): + """ + Return a dict of the data needs for render of each block. + """ + can_edit = has_studio_write_access(request.user, block.usage_key.course_key) + + return { + "is_unit_page": False, + "can_edit": can_edit, + "root_xblock": xblock, + "reorderable_items": set(), + "paging": None, + "force_render": None, + "item_url": "/container/{block.location}", + "tags_count_map": {}, + } + + try: + block = _load_preview_block(request, xblock) + preview_view = AUTHOR_VIEW if has_author_view(block) else STUDENT_VIEW + render_context = get_xblock_render_context(request, block) + block.render(preview_view, render_context) + except Exception as exc: # pylint: disable=broad-except + return str(exc) + + return ""