From b28d96e94da1813f75a920c950169cf4772e34c0 Mon Sep 17 00:00:00 2001 From: Alex Dusenbery Date: Wed, 25 Oct 2023 11:54:01 -0400 Subject: [PATCH] feat: allow clients of customer content_metadata view to skip ent customer fetches. ENT-7864 | Allows clients of the EnterpriseCustomerViewSet.content_metadata view to include an optional `skip_customer_fetch` query param. If present and truthy, including this param will cause the serialized content metadata to not fetch related enterprise customer details from the edx-enterprise REST API. Thus the presence of this query param means that the serialized 'content_last_modified' time will not take into account the *customer* modified time. Additionally, it means that no 'enrollment_url' fields will be present in the serialied response. --- enterprise_catalog/apps/api/v1/serializers.py | 11 ++++++++--- .../apps/api/v1/tests/test_views.py | 19 ++++++++++++++++--- .../apps/api/v1/views/enterprise_customer.py | 18 +++++++++++++++--- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/enterprise_catalog/apps/api/v1/serializers.py b/enterprise_catalog/apps/api/v1/serializers.py index 9c8eb59ee..3ec2acf45 100644 --- a/enterprise_catalog/apps/api/v1/serializers.py +++ b/enterprise_catalog/apps/api/v1/serializers.py @@ -220,8 +220,11 @@ def to_representation(self, instance): """ catalog_modified = None customer_modified = None - if enterprise_catalog := self.context.get('enterprise_catalog'): + enterprise_catalog = self.context.get('enterprise_catalog') + + if enterprise_catalog: catalog_modified = enterprise_catalog.modified + if enterprise_catalog and not self.context.get('skip_customer_fetch'): customer_modified = enterprise_catalog.enterprise_customer.last_modified_date content_type = instance.content_type @@ -255,7 +258,9 @@ def to_representation(self, instance): if content_type in (COURSE, COURSE_RUN): if enterprise_catalog: - json_metadata['enrollment_url'] = enterprise_catalog.get_content_enrollment_url(instance) + json_metadata['enrollment_url'] = None + if not self.context.get('skip_customer_fetch'): + json_metadata['enrollment_url'] = enterprise_catalog.get_content_enrollment_url(instance) json_metadata['xapi_activity_id'] = enterprise_catalog.get_xapi_activity_id( content_resource=content_type, content_key=content_key, @@ -267,7 +272,7 @@ def to_representation(self, instance): # for exec-ed-2u content, because enrollment fulfillment for such content # is controlled via Entitlements, which are tied directly to Courses # (as opposed to Seats, which are tied to Course Runs). - if not instance.is_exec_ed_2u_course: + if not instance.is_exec_ed_2u_course and not self.context.get('skip_customer_fetch'): self._add_course_run_enrollment_urls(instance, serialized_course_runs) elif content_type == PROGRAM: # We want this to be null, because we have no notion diff --git a/enterprise_catalog/apps/api/v1/tests/test_views.py b/enterprise_catalog/apps/api/v1/tests/test_views.py index 7a085f58d..d500960e6 100644 --- a/enterprise_catalog/apps/api/v1/tests/test_views.py +++ b/enterprise_catalog/apps/api/v1/tests/test_views.py @@ -2175,20 +2175,33 @@ def setUp(self): self.addCleanup(self.customer_details_patcher.stop) - def test_content_metadata_get_item_with_content_key(self): + @ddt.data(True, False) + def test_content_metadata_get_item_with_content_key(self, skip_customer_fetch): """ Test the base success case for the `content-metadata` view using a content key as an identifier """ - response = self.client.get(urljoin(self.url, f"{self.content_key_1}/")) + self.mock_customer_details.reset_mock() + query_params = '' + if skip_customer_fetch: + query_params = '?skip_customer_fetch=1' + response = self.client.get(urljoin(self.url, f"{self.content_key_1}/") + query_params) assert response.status_code == 200 expected_data = ContentMetadataSerializer( self.first_content_metadata, - context={'enterprise_catalog': self.enterprise_catalog}, + context={ + 'enterprise_catalog': self.enterprise_catalog, + 'skip_customer_fetch': skip_customer_fetch, + }, ).data actual_data = response.json() for payload_key in ['key', 'uuid']: assert actual_data[payload_key] == expected_data[payload_key] + if skip_customer_fetch: + self.assertFalse(self.mock_customer_details.called) + else: + self.assertTrue(self.mock_customer_details.called) + def test_content_metadata_get_item_with_content_key_in_multiple_catalogs(self): """ Test the base success case for the `content-metadata` view using a content key as an identifier diff --git a/enterprise_catalog/apps/api/v1/views/enterprise_customer.py b/enterprise_catalog/apps/api/v1/views/enterprise_customer.py index 52ff4b64d..c20ffa7d7 100644 --- a/enterprise_catalog/apps/api/v1/views/enterprise_customer.py +++ b/enterprise_catalog/apps/api/v1/views/enterprise_customer.py @@ -154,6 +154,9 @@ def get_metadata_item_serializer(self): enterprise_uuid=self.kwargs.get('enterprise_uuid') )) content_identifier = self.kwargs.get('content_identifier') + serializer_context = { + 'skip_customer_fetch': bool(self.request.query_params.get('skip_customer_fetch', '').lower()), + } try: # Search for matching metadata if the value of the requested @@ -164,7 +167,7 @@ def get_metadata_item_serializer(self): if content_with_uuid: return ContentMetadataSerializer( content_with_uuid.first(), - context={'enterprise_catalog': catalog}, + context={'enterprise_catalog': catalog, **serializer_context}, ) except ValueError: # Otherwise, search for matching metadata as a content key @@ -173,7 +176,7 @@ def get_metadata_item_serializer(self): if content_with_key: return ContentMetadataSerializer( content_with_key.first(), - context={'enterprise_catalog': catalog}, + context={'enterprise_catalog': catalog, **serializer_context}, ) # If we've made it here without finding a matching ContentMetadata record, # assume no matching record exists and raise a 404. @@ -183,7 +186,16 @@ def get_metadata_item_serializer(self): def content_metadata(self, customer_uuid, content_identifier, **kwargs): # pylint: disable=unused-argument """ Get endpoint for `/api/v1/enterprise-customer/{customer uuid}/content-metadata/{content identifier}`. - Accepts both content uuids and content keys for the specific content metadata record requested + Accepts both content uuids and content keys for the specific content metadata record requested. + + Accepts an optional `skip_customer_fetch` query parameter. + If present and truthy, including this param will cause + the serialized content metadata to not fetch related enterprise customer + details from the edx-enterprise REST API. Thus the presence of this + query param means that the serialized 'content_last_modified' time + will not take into account the *customer* modified time. Additionally, + it means that no 'enrollment_url' fields will be present in the serialied + response. """ serializer = self.get_metadata_item_serializer() return Response(serializer.data)