From 036ae6354fae1db9e267773bd7b881c42de1aa44 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Tue, 20 Aug 2024 19:42:44 +0500 Subject: [PATCH 01/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/api.py | 13 +++++++++---- lms/djangoapps/instructor/views/api_urls.py | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 2813daa8a318..59b9b487b93c 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2322,9 +2322,8 @@ def get(self, request, course_id): return _list_instructor_tasks(request=request, course_id=course_id) -@require_POST -@ensure_csrf_cookie -def list_instructor_tasks(request, course_id): +@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch') +class ListInstructorTasks(APIView): """ List instructor tasks. @@ -2334,7 +2333,13 @@ def list_instructor_tasks(request, course_id): - `problem_location_str` and `unique_student_identifier` lists task history for problem AND student (intersection) """ - return _list_instructor_tasks(request=request, course_id=course_id) + + @method_decorator(ensure_csrf_cookie) + """ + List instructor tasks. + """ + def post(self, request, course_id): + return _list_instructor_tasks(request=request, course_id=course_id) @cache_control(no_cache=True, no_store=True, must_revalidate=True) diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py index 599aead57303..056c835d7dc1 100644 --- a/lms/djangoapps/instructor/views/api_urls.py +++ b/lms/djangoapps/instructor/views/api_urls.py @@ -44,7 +44,7 @@ name='list_entrance_exam_instructor_tasks'), path('mark_student_can_skip_entrance_exam', api.mark_student_can_skip_entrance_exam, name='mark_student_can_skip_entrance_exam'), - path('list_instructor_tasks', api.list_instructor_tasks, name='list_instructor_tasks'), + path('list_instructor_tasks', api.ListInstructorTasks.as_view(), name='list_instructor_tasks'), path('list_background_email_tasks', api.list_background_email_tasks, name='list_background_email_tasks'), path('list_email_content', api.list_email_content, name='list_email_content'), path('list_forum_members', api.list_forum_members, name='list_forum_members'), From a939e586adf922113fd33f175187161cce003735 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Tue, 20 Aug 2024 22:33:51 +0500 Subject: [PATCH 02/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 6 ++++-- lms/djangoapps/instructor/views/api.py | 7 +++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index b0e533ee6f7f..b0e74df2174c 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -3808,9 +3808,11 @@ def test_list_instructor_tasks_problem(self, endpoint, act): assert actual_tasks == expected_tasks @patch('lms.djangoapps.instructor_task.api.get_instructor_task_history') - @ddt.data('list_instructor_tasks', 'instructor_api_v1:list_instructor_tasks') - def test_list_instructor_tasks_problem_student(self, endpoint, act): + def test_list_instructor_tasks_problem_student(self, act): """ Test list task history for problem AND student. """ + endpoint = 'list_instructor_tasks' + import pdb; + pdb.set_trace() act.return_value = self.tasks url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) mock_factory = MockCompletionInfo() diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 59b9b487b93c..521e067af225 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2333,12 +2333,11 @@ class ListInstructorTasks(APIView): - `problem_location_str` and `unique_student_identifier` lists task history for problem AND student (intersection) """ - @method_decorator(ensure_csrf_cookie) - """ - List instructor tasks. - """ def post(self, request, course_id): + """ + List instructor tasks. + """ return _list_instructor_tasks(request=request, course_id=course_id) From 1b4712d59dc953e2c78750a9401c17e6845f3c23 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Aug 2024 08:28:55 +0500 Subject: [PATCH 03/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 6 ++---- lms/djangoapps/instructor/views/api.py | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index b0e74df2174c..b0e533ee6f7f 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -3808,11 +3808,9 @@ def test_list_instructor_tasks_problem(self, endpoint, act): assert actual_tasks == expected_tasks @patch('lms.djangoapps.instructor_task.api.get_instructor_task_history') - def test_list_instructor_tasks_problem_student(self, act): + @ddt.data('list_instructor_tasks', 'instructor_api_v1:list_instructor_tasks') + def test_list_instructor_tasks_problem_student(self, endpoint, act): """ Test list task history for problem AND student. """ - endpoint = 'list_instructor_tasks' - import pdb; - pdb.set_trace() act.return_value = self.tasks url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) mock_factory = MockCompletionInfo() diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 521e067af225..c301450a24fa 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -105,7 +105,9 @@ from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError from lms.djangoapps.instructor_task.data import InstructorTaskTypes from lms.djangoapps.instructor_task.models import ReportStore -from lms.djangoapps.instructor.views.serializer import RoleNameSerializer, UserSerializer, AccessSerializer +from lms.djangoapps.instructor.views.serializer import ( + RoleNameSerializer, UserSerializer, AccessSerializer +) from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted from openedx.core.djangoapps.course_groups.models import CourseUserGroup @@ -2333,17 +2335,22 @@ class ListInstructorTasks(APIView): - `problem_location_str` and `unique_student_identifier` lists task history for problem AND student (intersection) """ + @method_decorator(ensure_csrf_cookie) def post(self, request, course_id): """ List instructor tasks. """ - return _list_instructor_tasks(request=request, course_id=course_id) + # serializer_data = AccessSerializer(data=request.data) + + return _list_instructor_tasks( + request=request, course_id=course_id + ) @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_course_permission(permissions.SHOW_TASKS) -def _list_instructor_tasks(request, course_id): +def _list_instructor_tasks(request, course_id, problem_location_str=None, unique_student_identifier=None): """ List instructor tasks. @@ -2351,8 +2358,14 @@ def _list_instructor_tasks(request, course_id): """ course_id = CourseKey.from_string(course_id) params = getattr(request, 'query_params', request.POST) + problem_location_str = strip_if_string(params.get('problem_location_str', False)) student = params.get('unique_student_identifier', None) + + if not student and not problem_location_str: # APIVIEW is not sending data as query_params + problem_location_str = strip_if_string(request.data.get('problem_location_str')) + student = request.data.get('unique_student_identifier') + if student is not None: student = get_student_from_identifier(student) From 55dd99ffa5164377c000915a9b1d8fdfab89851f Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Aug 2024 09:12:23 +0500 Subject: [PATCH 04/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index c301450a24fa..a8dc3187c32e 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2362,7 +2362,7 @@ def _list_instructor_tasks(request, course_id, problem_location_str=None, unique problem_location_str = strip_if_string(params.get('problem_location_str', False)) student = params.get('unique_student_identifier', None) - if not student and not problem_location_str: # APIVIEW is not sending data as query_params + if not student and not problem_location_str: # DRF is not receiving data as query_params:QueryDict problem_location_str = strip_if_string(request.data.get('problem_location_str')) student = request.data.get('unique_student_identifier') From 89741fc4ee8d128d502eb136f388db6d92dac30b Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Aug 2024 16:06:16 +0500 Subject: [PATCH 05/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/api.py | 10 +++-- lms/djangoapps/instructor/views/serializer.py | 38 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index a8dc3187c32e..a188cacc41a0 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -106,7 +106,7 @@ from lms.djangoapps.instructor_task.data import InstructorTaskTypes from lms.djangoapps.instructor_task.models import ReportStore from lms.djangoapps.instructor.views.serializer import ( - RoleNameSerializer, UserSerializer, AccessSerializer + AccessSerializer, ListInstructorSerializer, RoleNameSerializer, UserSerializer ) from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted @@ -2335,13 +2335,17 @@ class ListInstructorTasks(APIView): - `problem_location_str` and `unique_student_identifier` lists task history for problem AND student (intersection) """ + permission_classes = (IsAuthenticated, permissions.InstructorPermission) + permission_name = permissions.SHOW_TASKS + serializer_class = ListInstructorSerializer @method_decorator(ensure_csrf_cookie) def post(self, request, course_id): """ List instructor tasks. """ - # serializer_data = AccessSerializer(data=request.data) + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) return _list_instructor_tasks( request=request, course_id=course_id @@ -2350,7 +2354,7 @@ def post(self, request, course_id): @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_course_permission(permissions.SHOW_TASKS) -def _list_instructor_tasks(request, course_id, problem_location_str=None, unique_student_identifier=None): +def _list_instructor_tasks(request, course_id): """ List instructor tasks. diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py index 463f05ad45b8..ecb4b3330220 100644 --- a/lms/djangoapps/instructor/views/serializer.py +++ b/lms/djangoapps/instructor/views/serializer.py @@ -59,3 +59,41 @@ def validate_unique_student_identifier(self, value): return None return user + + +class ListInstructorSerializer(serializers.Serializer): # pylint: disable=abstract-method + """ + Serializer for handling the input data for the problem response report generation API. + +Attributes: + unique_student_identifier (str): The email or username of the student. + This field is optional, but if provided, the `problem_location_str` + must also be provided. + problem_location_str (str): The string representing the location of the problem within the course. + This field is optional, unless `unique_student_identifier` is provided. + """ + unique_student_identifier = serializers.CharField( + max_length=255, + help_text="Email or username of student", + required=False + ) + problem_location_str = serializers.CharField( + max_length=255, + help_text="Problem location", + required=False + ) + + def validate(self, data): + """ + Validate the data to ensure that if unique_student_identifier is provided, + problem_location_str must also be provided. + """ + unique_student_identifier = data.get('unique_student_identifier') + problem_location_str = data.get('problem_location_str') + + if unique_student_identifier and not problem_location_str: + raise serializers.ValidationError( + "unique_student_identifier must accompany problem_location_str" + ) + + return data From 40034241eb0bd82ac39e446a49d938cde578f505 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Thu, 22 Aug 2024 21:24:56 +0500 Subject: [PATCH 06/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/api.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index a188cacc41a0..ef8f0510c3bb 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2362,14 +2362,9 @@ def _list_instructor_tasks(request, course_id): """ course_id = CourseKey.from_string(course_id) params = getattr(request, 'query_params', request.POST) - problem_location_str = strip_if_string(params.get('problem_location_str', False)) student = params.get('unique_student_identifier', None) - if not student and not problem_location_str: # DRF is not receiving data as query_params:QueryDict - problem_location_str = strip_if_string(request.data.get('problem_location_str')) - student = request.data.get('unique_student_identifier') - if student is not None: student = get_student_from_identifier(student) From 637599c8d74a9afa2172972f280150dfe8368700 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Fri, 23 Aug 2024 11:14:10 +0500 Subject: [PATCH 07/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/api.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index ef8f0510c3bb..861e553c0933 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2360,11 +2360,21 @@ def _list_instructor_tasks(request, course_id): Internal function with common code for both DRF and and tradition views. """ + # This method is also used by other APIs with the GET method. + # The query_params attribute is utilized for GET requests, + # where parameters are passed as query strings. + course_id = CourseKey.from_string(course_id) params = getattr(request, 'query_params', request.POST) problem_location_str = strip_if_string(params.get('problem_location_str', False)) student = params.get('unique_student_identifier', None) + # For the DRF POST method, retrieve the data from request.data + if not student and not problem_location_str: + params = getattr(request, 'data', request.POST) + problem_location_str = strip_if_string(params.get('problem_location_str')) + student = params.get('unique_student_identifier') + if student is not None: student = get_student_from_identifier(student) From eaafd165f01c0acea817e3cfef71abb8d0fc4bc5 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Fri, 23 Aug 2024 11:18:47 +0500 Subject: [PATCH 08/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 861e553c0933..2577b2abf21f 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2372,8 +2372,8 @@ def _list_instructor_tasks(request, course_id): # For the DRF POST method, retrieve the data from request.data if not student and not problem_location_str: params = getattr(request, 'data', request.POST) - problem_location_str = strip_if_string(params.get('problem_location_str')) - student = params.get('unique_student_identifier') + problem_location_str = strip_if_string(params.get('problem_location_str', False)) + student = params.get('unique_student_identifier', None) if student is not None: student = get_student_from_identifier(student) From 5f4307ef4e19356b12ff8b12e4e1a8b39f11667e Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Fri, 23 Aug 2024 11:45:47 +0500 Subject: [PATCH 09/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/serializer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py index ecb4b3330220..dee0c13a8686 100644 --- a/lms/djangoapps/instructor/views/serializer.py +++ b/lms/djangoapps/instructor/views/serializer.py @@ -78,7 +78,6 @@ class ListInstructorSerializer(serializers.Serializer): # pylint: disable=abstr required=False ) problem_location_str = serializers.CharField( - max_length=255, help_text="Problem location", required=False ) From 51800f7422f5ea46e65724f08747b6ffe07c25bd Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Tue, 27 Aug 2024 16:50:07 +0500 Subject: [PATCH 10/12] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/views/api.py | 20 +++++++++---------- lms/djangoapps/instructor/views/serializer.py | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 2577b2abf21f..2cc81df5d6b3 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -106,7 +106,7 @@ from lms.djangoapps.instructor_task.data import InstructorTaskTypes from lms.djangoapps.instructor_task.models import ReportStore from lms.djangoapps.instructor.views.serializer import ( - AccessSerializer, ListInstructorSerializer, RoleNameSerializer, UserSerializer + AccessSerializer, ListInstructorTaskInputSerializer, RoleNameSerializer, UserSerializer ) from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted @@ -2337,7 +2337,7 @@ class ListInstructorTasks(APIView): """ permission_classes = (IsAuthenticated, permissions.InstructorPermission) permission_name = permissions.SHOW_TASKS - serializer_class = ListInstructorSerializer + serializer_class = ListInstructorTaskInputSerializer @method_decorator(ensure_csrf_cookie) def post(self, request, course_id): @@ -2348,13 +2348,13 @@ def post(self, request, course_id): serializer.is_valid(raise_exception=True) return _list_instructor_tasks( - request=request, course_id=course_id + request=request, course_id=course_id, serialize_data= serializer.validated_data ) @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_course_permission(permissions.SHOW_TASKS) -def _list_instructor_tasks(request, course_id): +def _list_instructor_tasks(request, course_id, serialize_data=None): """ List instructor tasks. @@ -2365,13 +2365,11 @@ def _list_instructor_tasks(request, course_id): # where parameters are passed as query strings. course_id = CourseKey.from_string(course_id) - params = getattr(request, 'query_params', request.POST) - problem_location_str = strip_if_string(params.get('problem_location_str', False)) - student = params.get('unique_student_identifier', None) - - # For the DRF POST method, retrieve the data from request.data - if not student and not problem_location_str: - params = getattr(request, 'data', request.POST) + if serialize_data is not None: + problem_location_str = strip_if_string(serialize_data.get('problem_location_str', False)) + student = serialize_data.get('unique_student_identifier', None) + else: + params = getattr(request, 'query_params', request.POST) problem_location_str = strip_if_string(params.get('problem_location_str', False)) student = params.get('unique_student_identifier', None) diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py index dee0c13a8686..47b7a7c9e4be 100644 --- a/lms/djangoapps/instructor/views/serializer.py +++ b/lms/djangoapps/instructor/views/serializer.py @@ -61,7 +61,7 @@ def validate_unique_student_identifier(self, value): return user -class ListInstructorSerializer(serializers.Serializer): # pylint: disable=abstract-method +class ListInstructorTaskInputSerializer(serializers.Serializer): # pylint: disable=abstract-method """ Serializer for handling the input data for the problem response report generation API. From ae2a76872485fe8a77cb0beb93ba46492bde1b83 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Fri, 13 Sep 2024 11:49:48 +0500 Subject: [PATCH 11/12] feat!: upgrading simple api with DRF. --- lms/djangoapps/instructor/views/api.py | 2 +- lms/djangoapps/instructor/views/serializer.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index d0442b80cbee..58556ee9ab02 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2397,7 +2397,7 @@ def post(self, request, course_id): serializer.is_valid(raise_exception=True) return _list_instructor_tasks( - request=request, course_id=course_id, serialize_data= serializer.validated_data + request=request, course_id=course_id, serialize_data=serializer.validated_data ) diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py index 892c0fb82c34..da91eba43124 100644 --- a/lms/djangoapps/instructor/views/serializer.py +++ b/lms/djangoapps/instructor/views/serializer.py @@ -215,4 +215,3 @@ def validate_student(self, value): return None return user - From 5be86712e37ac7e4dae5fb641f9e39da5e4e37c0 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Fri, 13 Sep 2024 12:40:30 +0500 Subject: [PATCH 12/12] feat!: updating tests. --- lms/djangoapps/instructor/tests/test_api.py | 27 +++++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 6e0a2545f530..e8bcc81318da 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4704,15 +4704,19 @@ class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollm Test endpoints using Oauth2 authentication. """ - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.course = CourseFactory.create( - entrance_exam_id='i4x://{}/{}/chapter/Entrance_exam'.format('test_org', 'test_course') - ) - def setUp(self): super().setUp() + self.course = CourseFactory.create( + org='test_org', + course='test_course', + run='test_run', + entrance_exam_id='i4x://{}/{}/chapter/Entrance_exam'.format('test_org', 'test_course') + ) + self.problem_location = msk_from_problem_urlname( + self.course.id, + 'robot-some-problem-urlname' + ) + self.problem_urlname = str(self.problem_location) self.other_user = UserFactory() dot_application = ApplicationFactory(user=self.other_user, authorization_grant_type='password') @@ -4744,7 +4748,14 @@ def setUp(self): "send-to": ["myself"], "subject": "This is subject", "message": "message" - }, 'data_researcher') + }, 'data_researcher'), + ('list_instructor_tasks', + { + 'problem_location_str': self.problem_urlname, + 'unique_student_identifier': self.other_user.email + }, + 'data_researcher'), + ('list_instructor_tasks', {}, 'data_researcher') ] self.fake_jwt = ('wyJUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjaGFuZ2UtbWUiLCJleHAiOjE3MjU4OTA2NzIsImdyY'