From 32fa28faa7a3831710ba2685f69e826c5538a921 Mon Sep 17 00:00:00 2001 From: Adam Stankiewicz Date: Fri, 22 Nov 2024 17:26:37 -0500 Subject: [PATCH] feat: expose enterprise_customer and enterprise_features in BFF response (#597) --- .../apps/api/v1/tests/test_bff_views.py | 36 ++- .../apps/api/v1/views/bffs/common.py | 4 +- .../apps/bffs/response_builder.py | 62 +++-- enterprise_access/apps/bffs/serializers.py | 221 ++++++++++++------ .../apps/bffs/tests/test_handlers.py | 4 +- .../apps/bffs/tests/test_response_builders.py | 52 +++-- enterprise_access/apps/bffs/tests/utils.py | 69 +++++- 7 files changed, 318 insertions(+), 130 deletions(-) diff --git a/enterprise_access/apps/api/v1/tests/test_bff_views.py b/enterprise_access/apps/api/v1/tests/test_bff_views.py index da5194a0..e0bbd917 100644 --- a/enterprise_access/apps/api/v1/tests/test_bff_views.py +++ b/enterprise_access/apps/api/v1/tests/test_bff_views.py @@ -80,6 +80,12 @@ def setUp(self): } self.mock_enterprise_course_enrollments = [] + self.expected_enterprise_customer = { + **self.mock_enterprise_customer, + 'disable_search': False, + 'show_integration_warning': True, + } + self.expected_customer_agreement = { 'uuid': self.mock_customer_agreement_uuid, 'available_subscription_catalogs': self.mock_customer_agreement.get('available_subscription_catalogs'), @@ -126,7 +132,8 @@ def setUp(self): } # Mock base response data - self.mock_dashboard_route_response_data = { + self.mock_common_response_data = { + 'enterprise_customer': self.expected_enterprise_customer, 'enterprise_customer_user_subsidies': { 'subscriptions': { 'customer_agreement': None, @@ -139,9 +146,13 @@ def setUp(self): }, }, }, - 'enterprise_course_enrollments': [], 'errors': [], 'warnings': [], + 'enterprise_features': {'feature_flag': True}, + } + self.mock_dashboard_route_response_data = { + **self.mock_common_response_data, + 'enterprise_course_enrollments': [], } @ddt.data( @@ -434,9 +445,21 @@ def test_dashboard_with_subscriptions_license_auto_apply( 'system_wide_role': SYSTEM_ENTERPRISE_LEARNER_ROLE, 'context': self.mock_enterprise_customer_uuid, }]) + mock_identity_provider = 'mock_idp' if identity_provider else None + mock_identity_providers = ( + [ + { + 'provider_id': 'mock_idp', + 'default_provider': True, + }, + ] + if identity_provider + else [] + ) mock_enterprise_customer_with_auto_apply = { **self.mock_enterprise_customer, - 'identity_provider': identity_provider, + 'identity_provider': mock_identity_provider, + 'identity_providers': mock_identity_providers, } mock_enterprise_learner_response_data = { **self.mock_enterprise_learner_response_data, @@ -493,7 +516,14 @@ def test_dashboard_with_subscriptions_license_auto_apply( } expected_licenses = [expected_activated_subscription_license] if should_auto_apply else [] expected_response_data = self.mock_dashboard_route_response_data.copy() + expected_show_integration_warning = bool(identity_provider) expected_response_data.update({ + 'enterprise_customer': { + **self.expected_enterprise_customer, + 'identity_provider': mock_identity_provider, + 'identity_providers': mock_identity_providers, + 'show_integration_warning': expected_show_integration_warning, + }, 'enterprise_customer_user_subsidies': { 'subscriptions': { 'customer_agreement': expected_customer_agreement, diff --git a/enterprise_access/apps/api/v1/views/bffs/common.py b/enterprise_access/apps/api/v1/views/bffs/common.py index 83ac0562..cebc0150 100644 --- a/enterprise_access/apps/api/v1/views/bffs/common.py +++ b/enterprise_access/apps/api/v1/views/bffs/common.py @@ -78,10 +78,12 @@ def load_route_data_and_build_response(self, request, handler_class, response_bu ordered_representation = OrderedDict(response_data) - # Remove errors and warnings from the response, and add them back at the end + # Remove errors/warnings & enterprise_features from the response, and add them back at the end errors = ordered_representation.pop('errors', []) warnings = ordered_representation.pop('warnings', []) + enterprise_features = ordered_representation.pop('enterprise_features', {}) ordered_representation['errors'] = errors ordered_representation['warnings'] = warnings + ordered_representation['enterprise_features'] = enterprise_features return dict(ordered_representation), status_code diff --git a/enterprise_access/apps/bffs/response_builder.py b/enterprise_access/apps/bffs/response_builder.py index 5a7f1e91..984f2d6f 100644 --- a/enterprise_access/apps/bffs/response_builder.py +++ b/enterprise_access/apps/bffs/response_builder.py @@ -18,6 +18,13 @@ class BaseResponseBuilder: response builders like `LearnerDashboardResponseBuilder` or `CourseResponseBuilder`. """ + @property + def status_code(self): + """ + Returns the HTTP status code for the response from HandlerContext. + """ + return self.context.status_code + def __init__(self, context): """ Initializes the BaseResponseBuilder with a HandlerContext. @@ -26,6 +33,7 @@ def __init__(self, context): context (HandlerContext): The context object containing data, errors, and request information. """ self.context = context + self.response_data = {} def build(self): """ @@ -35,25 +43,16 @@ def build(self): Returns: dict: A dictionary containing the response data. """ - raise NotImplementedError("Subclasses must implement the `build` method.") + self.response_data['enterprise_customer'] = self.context.enterprise_customer + self.response_data['enterprise_features'] = self.context.enterprise_features + return self.response_data, self.status_code - def add_errors_warnings_to_response(self, response_data): + def add_errors_warnings_to_response(self): """ Adds any errors to the response data. """ - response_data['errors'] = self.context.errors - response_data['warnings'] = self.context.warnings - return response_data - - # TODO Revisit this function in ENT-9633 to determine if 200 is ok for a nested errored response - def get_status_code(self): - """ - Gets the current status code from the context. - - Returns: - int: The HTTP status code. - """ - return self.context.status_code + self.response_data['errors'] = self.context.errors + self.response_data['warnings'] = self.context.warnings class BaseLearnerResponseBuilder(BaseResponseBuilder, BaseLearnerDataMixin): @@ -64,7 +63,7 @@ class BaseLearnerResponseBuilder(BaseResponseBuilder, BaseLearnerDataMixin): for building responses across all learner-focused page routes. """ - def common_response_logic(self, response_data=None): + def common_response_logic(self): """ Applies common response logic for learner-related responses. @@ -74,12 +73,7 @@ def common_response_logic(self, response_data=None): Returns: dict: The modified response data with common logic applied. """ - if not response_data: - response_data = {} - - response_data['enterprise_customer_user_subsidies'] = self.enterprise_customer_user_subsidies - - return response_data + self.response_data['enterprise_customer_user_subsidies'] = self.enterprise_customer_user_subsidies def build(self): """ @@ -90,14 +84,16 @@ def build(self): Returns: dict: A tuple containing the base response data and status code. """ + super().build() + # Initialize response data with common learner-related logic - response_data = self.common_response_logic() + self.common_response_logic() # Add any errors, etc. - response_data = self.add_errors_warnings_to_response(response_data) + self.add_errors_warnings_to_response() # Return the response data and status code - return response_data, self.get_status_code() + return self.response_data, self.status_code class LearnerDashboardResponseBuilder(BaseLearnerResponseBuilder, LearnerDashboardDataMixin): @@ -118,29 +114,31 @@ def build(self): dict: A tuple containing the learner dashboard serialized response data and status code. """ # Build common response data - response_data, __ = super().build() + super().build() # Add specific fields related to the learner dashboard - response_data.update({ + self.response_data.update({ 'enterprise_course_enrollments': self.enterprise_course_enrollments, }) # Add any errors and warnings to the response - response_data = self.add_errors_warnings_to_response(response_data) + self.add_errors_warnings_to_response() # Serialize and validate the response try: - serializer = LearnerDashboardResponseSerializer(data=response_data) + serializer = LearnerDashboardResponseSerializer(data=self.response_data) serializer.is_valid(raise_exception=True) serialized_data = serializer.validated_data # Return the response data and status code - return serialized_data, self.get_status_code() + return serialized_data, self.status_code except Exception as exc: # pylint: disable=broad-except logger.exception('Could not serialize the response data.') self.context.add_error( user_message='An error occurred while processing the response data.', developer_message=f'Could not serialize the response data. Error: {exc}', ) - response_data = self.add_errors_warnings_to_response(response_data) - return response_data, self.get_status_code() + self.add_errors_warnings_to_response() + serializer = LearnerDashboardResponseSerializer(self.response_data) + serialized_data = serializer.data + return serialized_data, self.status_code diff --git a/enterprise_access/apps/bffs/serializers.py b/enterprise_access/apps/bffs/serializers.py index 1a3896f3..91dd076b 100644 --- a/enterprise_access/apps/bffs/serializers.py +++ b/enterprise_access/apps/bffs/serializers.py @@ -5,7 +5,19 @@ from rest_framework import serializers -class BaseBFFMessageSerializer(serializers.Serializer): +class BaseBffSerializer(serializers.Serializer): + """ + Base Serializer for BFF. + """ + + def create(self, validated_data): + return validated_data + + def update(self, instance, validated_data): + return validated_data + + +class BaseBFFMessageSerializer(BaseBffSerializer): """ Base Serializer for BFF messages. @@ -16,12 +28,6 @@ class BaseBFFMessageSerializer(serializers.Serializer): developer_message = serializers.CharField() user_message = serializers.CharField() - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - class ErrorSerializer(BaseBFFMessageSerializer): pass @@ -31,22 +37,133 @@ class WarningSerializer(BaseBFFMessageSerializer): pass -class BaseResponseSerializer(serializers.Serializer): +class EnterpriseCustomerSiteSerializer(BaseBffSerializer): + """ + Serializer for enterprise customer site. + """ + + domain = serializers.CharField() + name = serializers.CharField() + + +class EnterpriseCustomerBrandingConfiguration(BaseBffSerializer): + """ + Serializer for enterprise customer branding configuration. + """ + + logo = serializers.URLField(required=False, allow_null=True) + primary_color = serializers.CharField() + secondary_color = serializers.CharField() + tertiary_color = serializers.CharField() + + +class EnterpriseCustomerNotificationBanner(BaseBffSerializer): + """ + Serializer for enterprise customer notification banner. + """ + title = serializers.CharField(required=False, allow_null=True, allow_blank=True) + text = serializers.CharField(required=False, allow_null=True, allow_blank=True) + + +class EnterpriseCustomerAdminUser(BaseBffSerializer): + """ + Serializer for enterprise customer admin user. + """ + email = serializers.EmailField(required=False, allow_null=True) + lms_user_id = serializers.IntegerField() + + +class EnterpriseCustomerActiveIntegration(BaseBffSerializer): + """ + Serializer for enterprise customer integration. + """ + channel_code = serializers.CharField() + created = serializers.DateTimeField() + modified = serializers.DateTimeField() + display_name = serializers.CharField() + active = serializers.BooleanField() + + +class EnterpriseCustomerIdentityProvider(BaseBffSerializer): + """ + Serializer for enterprise customer identity provider. + """ + provider_id = serializers.CharField() + default_provider = serializers.BooleanField() + + +class EnterpriseCustomerSerializer(BaseBffSerializer): + """ + Serializer for enterprise customer. + """ + + uuid = serializers.UUIDField() + slug = serializers.CharField() + name = serializers.CharField() + active = serializers.BooleanField() + auth_org_id = serializers.CharField(required=False, allow_null=True) + site = EnterpriseCustomerSiteSerializer() + branding_configuration = EnterpriseCustomerBrandingConfiguration() + identity_provider = serializers.CharField(required=False, allow_null=True) + identity_providers = serializers.ListField(child=EnterpriseCustomerIdentityProvider(), required=False, default=list) + enable_data_sharing_consent = serializers.BooleanField() + enforce_data_sharing_consent = serializers.CharField() + disable_expiry_messaging_for_learner_credit = serializers.BooleanField() + enable_audit_enrollment = serializers.BooleanField() + replace_sensitive_sso_username = serializers.BooleanField() + enable_portal_code_management_screen = serializers.BooleanField() + sync_learner_profile_data = serializers.BooleanField() + enable_audit_data_reporting = serializers.BooleanField() + enable_learner_portal = serializers.BooleanField() + enable_learner_portal_offers = serializers.BooleanField() + enable_portal_learner_credit_management_screen = serializers.BooleanField() + enable_executive_education_2U_fulfillment = serializers.BooleanField() + enable_portal_reporting_config_screen = serializers.BooleanField() + enable_portal_saml_configuration_screen = serializers.BooleanField() + contact_email = serializers.EmailField(required=False, allow_null=True) + enable_portal_subscription_management_screen = serializers.BooleanField() + hide_course_original_price = serializers.BooleanField() + enable_analytics_screen = serializers.BooleanField() + enable_integrated_customer_learner_portal_search = serializers.BooleanField() + enable_generation_of_api_credentials = serializers.BooleanField() + enable_portal_lms_configurations_screen = serializers.BooleanField() + sender_alias = serializers.CharField(required=False, allow_null=True) + enterprise_customer_catalogs = serializers.ListField(child=serializers.UUIDField(), required=False, default=list) + reply_to = serializers.EmailField(required=False, allow_null=True) + enterprise_notification_banner = EnterpriseCustomerNotificationBanner(required=False, allow_null=True) + hide_labor_market_data = serializers.BooleanField() + modified = serializers.DateTimeField() + enable_universal_link = serializers.BooleanField() + enable_browse_and_request = serializers.BooleanField() + admin_users = EnterpriseCustomerAdminUser(many=True, required=False, default=list) + enable_learner_portal_sidebar_message = serializers.BooleanField() + learner_portal_sidebar_content = serializers.CharField(required=False, allow_null=True, allow_blank=True) + enable_pathways = serializers.BooleanField() + enable_programs = serializers.BooleanField() + enable_demo_data_for_analytics_and_lpr = serializers.BooleanField() + enable_academies = serializers.BooleanField() + enable_one_academy = serializers.BooleanField() + active_integrations = EnterpriseCustomerActiveIntegration(many=True, required=False, default=list) + show_videos_in_learner_portal_search_results = serializers.BooleanField() + default_language = serializers.CharField(required=False, allow_null=True) + country = serializers.CharField() + enable_slug_login = serializers.BooleanField() + disable_search = serializers.BooleanField() + show_integration_warning = serializers.BooleanField() + + +class BaseResponseSerializer(BaseBffSerializer): """ Serializer for base response. """ + enterprise_customer = EnterpriseCustomerSerializer() errors = ErrorSerializer(many=True, required=False, default=list) warnings = WarningSerializer(many=True, required=False, default=list) + enterprise_features = serializers.DictField(required=False, default=dict) - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - -class CustomerAgreementSerializer(serializers.Serializer): +class CustomerAgreementSerializer(BaseBffSerializer): """ Serializer for customer agreement. """ @@ -58,19 +175,13 @@ class CustomerAgreementSerializer(serializers.Serializer): disable_expiration_notifications = serializers.BooleanField() enable_auto_applied_subscriptions_with_universal_link = serializers.BooleanField() subscription_for_auto_applied_licenses = serializers.UUIDField(allow_null=True) - has_custom_license_expiration_messaging_v2 = serializers.BooleanField() - button_label_in_modal_v2 = serializers.CharField(allow_null=True) - expired_subscription_modal_messaging_v2 = serializers.CharField(allow_null=True) - modal_header_text_v2 = serializers.CharField(allow_null=True) + has_custom_license_expiration_messaging_v2 = serializers.BooleanField(required=False, default=False) + button_label_in_modal_v2 = serializers.CharField(required=False, allow_null=True) + expired_subscription_modal_messaging_v2 = serializers.CharField(required=False, allow_null=True) + modal_header_text_v2 = serializers.CharField(required=False, allow_null=True) - def create(self, validated_data): - return validated_data - def update(self, instance, validated_data): - return validated_data - - -class SubscriptionPlanSerializer(serializers.Serializer): +class SubscriptionPlanSerializer(BaseBffSerializer): """ Serializer for subscription plan. """ @@ -86,14 +197,8 @@ class SubscriptionPlanSerializer(serializers.Serializer): days_until_expiration_including_renewals = serializers.IntegerField() should_auto_apply_licenses = serializers.BooleanField(allow_null=True) - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - -class SubscriptionLicenseSerializer(serializers.Serializer): +class SubscriptionLicenseSerializer(BaseBffSerializer): """ Serializer for subscription license. """ @@ -107,14 +212,8 @@ class SubscriptionLicenseSerializer(serializers.Serializer): activation_key = serializers.CharField(allow_null=True) subscription_plan = SubscriptionPlanSerializer() - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - -class SubscriptionLicenseStatusSerializer(serializers.Serializer): +class SubscriptionLicenseStatusSerializer(BaseBffSerializer): """ Serializer for subscription license status. """ @@ -124,14 +223,8 @@ class SubscriptionLicenseStatusSerializer(serializers.Serializer): expired = SubscriptionLicenseSerializer(many=True, required=False, default=list) revoked = SubscriptionLicenseSerializer(many=True, required=False, default=list) - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - -class SubscriptionsSerializer(serializers.Serializer): +class SubscriptionsSerializer(BaseBffSerializer): """ Serializer for enterprise customer user subsidies. """ @@ -140,28 +233,16 @@ class SubscriptionsSerializer(serializers.Serializer): subscription_licenses = SubscriptionLicenseSerializer(many=True, required=False, default=list) subscription_licenses_by_status = SubscriptionLicenseStatusSerializer(required=False) - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - -class EnterpriseCustomerUserSubsidiesSerializer(serializers.Serializer): +class EnterpriseCustomerUserSubsidiesSerializer(BaseBffSerializer): """ Serializer for enterprise customer user subsidies. """ subscriptions = SubscriptionsSerializer() - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - -class BaseLearnerPortalResponseSerializer(BaseResponseSerializer, serializers.Serializer): +class BaseLearnerPortalResponseSerializer(BaseResponseSerializer): """ Serializer for base learner portal response. """ @@ -169,7 +250,7 @@ class BaseLearnerPortalResponseSerializer(BaseResponseSerializer, serializers.Se enterprise_customer_user_subsidies = EnterpriseCustomerUserSubsidiesSerializer() -class EnterpriseCourseEnrollmentSerializer(serializers.Serializer): +class EnterpriseCourseEnrollmentSerializer(BaseBffSerializer): """ Serializer for enterprise course enrollment. """ @@ -198,14 +279,8 @@ class EnterpriseCourseEnrollmentSerializer(serializers.Serializer): allow_empty=True, ) - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - -class BFFRequestSerializer(serializers.Serializer): +class BFFRequestSerializer(BaseBffSerializer): """ Serializer for the BFF request. """ @@ -219,12 +294,6 @@ class BFFRequestSerializer(serializers.Serializer): help_text="The slug of the enterprise customer.", ) - def create(self, validated_data): - return validated_data - - def update(self, instance, validated_data): - return validated_data - class LearnerDashboardRequestSerializer(BFFRequestSerializer): """ @@ -232,7 +301,7 @@ class LearnerDashboardRequestSerializer(BFFRequestSerializer): """ -class LearnerDashboardResponseSerializer(BaseLearnerPortalResponseSerializer, serializers.Serializer): +class LearnerDashboardResponseSerializer(BaseLearnerPortalResponseSerializer): """ Serializer for the learner dashboard response. """ diff --git a/enterprise_access/apps/bffs/tests/test_handlers.py b/enterprise_access/apps/bffs/tests/test_handlers.py index 0aff1215..43dd0097 100644 --- a/enterprise_access/apps/bffs/tests/test_handlers.py +++ b/enterprise_access/apps/bffs/tests/test_handlers.py @@ -64,12 +64,12 @@ def setUp(self): self.expected_enterprise_customer = { **self.mock_enterprise_customer, 'disable_search': False, - 'show_integration_warning': False, + 'show_integration_warning': True, } self.expected_enterprise_customer_2 = { **self.mock_enterprise_customer_2, 'disable_search': False, - 'show_integration_warning': False, + 'show_integration_warning': True, } self.mock_subscription_licenses_data = { 'customer_agreement': None, diff --git a/enterprise_access/apps/bffs/tests/test_response_builders.py b/enterprise_access/apps/bffs/tests/test_response_builders.py index f0980ff1..e5ec3d52 100644 --- a/enterprise_access/apps/bffs/tests/test_response_builders.py +++ b/enterprise_access/apps/bffs/tests/test_response_builders.py @@ -14,13 +14,27 @@ @ddt.ddt class TestBaseResponseBuilder(TestHandlerContextMixin): + """ + Tests for BaseResponseBuilder. + """ @mock.patch('enterprise_access.apps.bffs.context.HandlerContext') def test_base_build_error(self, mock_handler_context): - mock_handler_context.return_value = self.mock_handler_context - base_response_builder = BaseResponseBuilder(mock_handler_context) - with self.assertRaises(NotImplementedError): - base_response_builder.build() + mock_context_data = { + 'enterprise_customer': self.mock_enterprise_customer, + } + mock_handler_context.return_value = self.get_mock_handler_context( + data=mock_context_data, + ) + mock_handler_context_instance = mock_handler_context.return_value + base_response_builder = BaseResponseBuilder(mock_handler_context_instance) + response_data, status_code = base_response_builder.build() + expected_response_data = { + 'enterprise_customer': self.mock_enterprise_customer, + 'enterprise_features': {'feature_flag': True}, + } + self.assertEqual(response_data, expected_response_data) + self.assertEqual(status_code, status.HTTP_200_OK) @ddt.data( { @@ -43,14 +57,17 @@ def test_base_build_error(self, mock_handler_context): @mock.patch('enterprise_access.apps.bffs.context.HandlerContext') @ddt.unpack def test_add_errors_warnings_to_response(self, mock_handler_context, errors, warnings): - mock_handler_context.return_value = self.mock_handler_context + mock_context_data = { + 'enterprise_customer': self.mock_enterprise_customer, + } + mock_handler_context.return_value = self.get_mock_handler_context( + data=mock_context_data, + ) mock_handler_context_instance = mock_handler_context.return_value base_response_builder = BaseResponseBuilder(mock_handler_context_instance) - sample_response = { - "some_existing_metadata": "should_still_exist" - } expected_output = { - **sample_response, + 'enterprise_customer': self.mock_enterprise_customer, + 'enterprise_features': {'feature_flag': True}, 'errors': [], 'warnings': [], } @@ -61,7 +78,8 @@ def test_add_errors_warnings_to_response(self, mock_handler_context, errors, war if warnings: mock_handler_context_instance.warnings.append(self.mock_warning) expected_output['warnings'] = [self.mock_warning] - response_data = base_response_builder.add_errors_warnings_to_response(sample_response) + base_response_builder.add_errors_warnings_to_response() + response_data, _ = base_response_builder.build() self.assertEqual(response_data, expected_output) # TODO Revisit this function in ENT-9633 to determine if 200 is ok for a nested errored response @@ -75,7 +93,7 @@ def test_add_errors_warnings_to_response(self, mock_handler_context, errors, war ) @mock.patch('enterprise_access.apps.bffs.context.HandlerContext') @ddt.unpack - def test_get_status_code(self, mock_handler_context, status_code): + def test_status_code(self, mock_handler_context, status_code): if status_code: mock_handler_context.return_value = self.get_mock_handler_context( _status_code=status_code @@ -85,7 +103,7 @@ def test_get_status_code(self, mock_handler_context, status_code): mock_handler_context_instance = mock_handler_context.return_value base_response_builder = BaseResponseBuilder(mock_handler_context_instance) expected_output = status_code if status_code else status.HTTP_200_OK - response_status_code = base_response_builder.get_status_code() + response_status_code = base_response_builder.status_code self.assertEqual(response_status_code, expected_output) @@ -109,9 +127,13 @@ def setUp(self): @mock.patch('enterprise_access.apps.bffs.context.HandlerContext') @ddt.unpack def test_build(self, mock_handler_context, has_subscriptions_data): - mock_handler_context.return_value = self.mock_handler_context + mock_context_data = { + 'enterprise_customer': self.mock_enterprise_customer, + } + mock_handler_context.return_value = self.get_mock_handler_context( + data=mock_context_data, + ) mock_handler_context_instance = mock_handler_context.return_value - base_learner_response_builder = BaseLearnerResponseBuilder(mock_handler_context_instance) mock_subscriptions_data = { "customer_agreement": None, @@ -139,6 +161,8 @@ def test_build(self, mock_handler_context, has_subscriptions_data): } expected_response = { + 'enterprise_customer': self.mock_enterprise_customer, + 'enterprise_features': {'feature_flag': True}, 'enterprise_customer_user_subsidies': { 'subscriptions': mock_subscriptions_data, }, diff --git a/enterprise_access/apps/bffs/tests/utils.py b/enterprise_access/apps/bffs/tests/utils.py index a0330bc7..d9774c5f 100644 --- a/enterprise_access/apps/bffs/tests/utils.py +++ b/enterprise_access/apps/bffs/tests/utils.py @@ -44,12 +44,75 @@ def setUp(self): self.mock_enterprise_customer = { 'uuid': self.mock_enterprise_customer_uuid, 'slug': self.mock_enterprise_customer_slug, + 'active': True, + 'name': 'Mock Enterprise Customer', 'enable_learner_portal': True, + 'site': { + 'domain': 'edX.org', + 'name': 'edX', + }, + 'branding_configuration': { + 'logo': 'https://edx.org/logo.png', + 'primary_color': '#000000', + 'secondary_color': '#000000', + 'tertiary_color': '#000000', + }, + 'enable_data_sharing_consent': True, + 'enforce_data_sharing_consent': 'at_enrollment', + 'disable_expiry_messaging_for_learner_credit': False, + 'enable_audit_enrollment': False, + 'replace_sensitive_sso_username': False, + 'enable_portal_code_management_screen': True, + 'sync_learner_profile_data': False, + 'enable_audit_data_reporting': False, + 'enable_learner_portal_offers': False, + 'enable_portal_learner_credit_management_screen': True, + 'enable_executive_education_2U_fulfillment': True, + 'enable_portal_reporting_config_screen': True, + 'enable_portal_saml_configuration_screen': True, + 'enable_portal_subscription_management_screen': True, + 'hide_course_original_price': False, + 'enable_analytics_screen': True, + 'enable_integrated_customer_learner_portal_search': True, + 'enable_generation_of_api_credentials': False, + 'enable_portal_lms_configurations_screen': True, + 'hide_labor_market_data': False, + 'modified': '2024-11-22T12:00:00Z', + 'enable_universal_link': True, + 'enable_browse_and_request': True, + 'enable_learner_portal_sidebar_message': False, + 'learner_portal_sidebar_content': None, + 'enable_pathways': True, + 'enable_programs': True, + 'enable_demo_data_for_analytics_and_lpr': False, + 'enable_academies': True, + 'enable_one_academy': False, + 'show_videos_in_learner_portal_search_results': True, + 'country': 'US', + 'enable_slug_login': False, + 'admin_users': [{ + 'email': 'admin@example.com', + 'lms_user_id': 12, + }], + 'active_integrations': [], + 'enterprise_customer_catalogs': [], + 'identity_provider': 'mock_idp', + 'identity_providers': [{ + 'provider_id': 'mock_idp', + 'default_provider': True, + }], + 'contact_email': None, + 'auth_org_id': None, + 'default_language': None, + 'enterprise_notification_banner': None, + 'reply_to': None, + 'sender_alias': None, } self.mock_enterprise_customer_2 = { + **self.mock_enterprise_customer, 'uuid': self.mock_enterprise_customer_uuid_2, 'slug': self.mock_enterprise_customer_slug_2, - 'enable_learner_portal': True, + 'name': 'Mock Enterprise Customer 2', } self.mock_enterprise_learner_response_data = { 'results': [ @@ -87,7 +150,7 @@ def get_mock_handler_context(self, **kwargs): '_enterprise_customer_uuid': self.mock_enterprise_customer_uuid, '_enterprise_customer_slug': self.mock_enterprise_customer_slug, '_lms_user_id': self.request.user.lms_user_id, - '_enterprise_features': {'feature_a': True}, + '_enterprise_features': {'feature_flag': True}, 'data': {}, } @@ -98,6 +161,7 @@ def get_mock_handler_context(self, **kwargs): mock_handler_context = mock.MagicMock(**default_values) # Define a dictionary of private attributes to property names + mock_property_enterprise_customer = getattr(mock_handler_context, 'data').get('enterprise_customer') property_mocks = { 'request': getattr(mock_handler_context, '_request'), 'status_code': getattr(mock_handler_context, '_status_code'), @@ -105,6 +169,7 @@ def get_mock_handler_context(self, **kwargs): 'warnings': getattr(mock_handler_context, '_warnings'), 'enterprise_customer_uuid': getattr(mock_handler_context, '_enterprise_customer_uuid'), 'enterprise_customer_slug': getattr(mock_handler_context, '_enterprise_customer_slug'), + 'enterprise_customer': mock_property_enterprise_customer, 'lms_user_id': getattr(mock_handler_context, '_lms_user_id'), 'enterprise_features': getattr(mock_handler_context, '_enterprise_features'), }