Skip to content

Commit

Permalink
feat: incorporate enterprise customer logic into BFF (#624)
Browse files Browse the repository at this point in the history
  • Loading branch information
brobro10000 authored Feb 21, 2025
1 parent 545c22e commit 20ba138
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 105 deletions.
215 changes: 139 additions & 76 deletions enterprise_access/apps/api/v1/tests/test_bff_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,25 @@ def setUp(self):
# Mock base response data
self.mock_common_response_data = {
'enterprise_customer': self.expected_enterprise_customer,
'all_linked_enterprise_customer_users': [
{
'id': 1,
'active': True,
'enterprise_customer': self.expected_enterprise_customer,
},
{
'id': 2,
'active': False,
'enterprise_customer': {
**self.mock_enterprise_customer_2,
'disable_search': False,
'show_integration_warning': True,
},
},
],
'active_enterprise_customer': self.expected_enterprise_customer,
'staff_enterprise_customer': None,
'should_update_active_enterprise_customer_user': self.mock_should_update_active_enterprise_customer_user,
'enterprise_customer_user_subsidies': {
'subscriptions': {
'customer_agreement': None,
Expand Down Expand Up @@ -242,7 +261,6 @@ def test_dashboard_empty_state_with_permissions(
expected_response_data = {
'detail': f'Missing: {BFF_READ_PERMISSION}',
}

self.assertEqual(response.status_code, expected_status_code)
self.assertEqual(response.json(), expected_response_data)

Expand Down Expand Up @@ -369,6 +387,104 @@ def test_dashboard_with_subscriptions_license_activation(
})
self.assertEqual(response.json(), expected_response_data)

def _set_up_mock_dashboard_with_subscriptions_license_auto_apply_data(
self,
mock_get_default_enrollment_intentions_learner_status,
mock_get_subscription_licenses_for_learner,
mock_get_enterprise_customers_for_user,
mock_get_enterprise_course_enrollments,
mock_get_enterprise_customer_data,
mock_auto_apply_license,
identity_provider,
is_staff_request_user,
has_plan_for_auto_apply,
has_existing_activated_license,
has_existing_revoked_license,
auto_apply_with_universal_link,
):
"""
This function facilitate mocking of the dashboard_with_subscriptions_license_auto_apply
Additional mocks or business logic updates should be handled here due to pylint errors.
"""
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.expected_enterprise_customer,
'identity_provider': mock_identity_provider,
'identity_providers': mock_identity_providers,
'show_integration_warning': bool(identity_provider)
}
staff_enterprise_customer = None
active_enterprise_customer = mock_enterprise_customer_with_auto_apply
if is_staff_request_user:
# Set the request user as a staff user and mock the enterprise customer response data.
self.user.is_staff = True
self.user.save()
mock_get_enterprise_customer_data.return_value = self.mock_enterprise_customer
staff_enterprise_customer = mock_enterprise_customer_with_auto_apply
active_enterprise_customer = None
mock_linked_enterprise_customer_users = (
[]
if is_staff_request_user
else [{
'id': 1,
'active': True,
'enterprise_customer': mock_enterprise_customer_with_auto_apply,
}]
)
mock_enterprise_learner_response_data = {
**self.mock_enterprise_learner_response_data,
'results': mock_linked_enterprise_customer_users,
}
mock_get_enterprise_customers_for_user.return_value = mock_enterprise_learner_response_data
mock_auto_applied_subscription_license = {
**self.mock_subscription_license,
'status': 'activated',
'activation_date': '2024-01-01T00:00:00Z',
}
mock_subscription_licenses_data = {
**self.mock_subscription_licenses_data,
'customer_agreement': {
**self.mock_customer_agreement,
'enable_auto_applied_subscriptions_with_universal_link': auto_apply_with_universal_link,
'subscription_for_auto_applied_licenses': (
self.mock_subscription_plan_uuid
if has_plan_for_auto_apply else None
),
},
}
if has_existing_activated_license:
mock_subscription_licenses_data['results'].append({
**self.mock_subscription_license,
'status': 'activated',
'activation_date': '2024-01-01T00:00:00Z',
})
if has_existing_revoked_license:
mock_subscription_licenses_data['results'].append({
**self.mock_subscription_license,
'status': 'revoked',
})
mock_get_subscription_licenses_for_learner.return_value = mock_subscription_licenses_data
mock_auto_apply_license.return_value = mock_auto_applied_subscription_license
mock_get_default_enrollment_intentions_learner_status.return_value = \
self.mock_default_enterprise_enrollment_intentions_learner_status_data
mock_get_enterprise_course_enrollments.return_value = self.mock_enterprise_course_enrollments
return (
mock_enterprise_customer_with_auto_apply,
mock_linked_enterprise_customer_users,
staff_enterprise_customer,
active_enterprise_customer,
)

@ddt.data(
# No existing licenses, identity provider, no universal link auto-apply, no plan for auto-apply,
# and not a staff request user.
Expand Down Expand Up @@ -533,75 +649,24 @@ def test_dashboard_with_subscriptions_license_auto_apply(
'system_wide_role': SYSTEM_ENTERPRISE_LEARNER_ROLE,
'context': self.mock_enterprise_customer_uuid,
}])

if is_staff_request_user:
# Set the request user as a staff user and mock the enterprise customer response data.
self.user.is_staff = True
self.user.save()
mock_get_enterprise_customer_data.return_value = self.mock_enterprise_customer

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': mock_identity_provider,
'identity_providers': mock_identity_providers,
}
mock_linked_enterprise_customer_users = (
[]
if is_staff_request_user
else [{
'active': True,
'enterprise_customer': mock_enterprise_customer_with_auto_apply,
}]
(mock_enterprise_customer_with_auto_apply,
mock_linked_enterprise_customer_users,
staff_enterprise_customer,
active_enterprise_customer,
) = self._set_up_mock_dashboard_with_subscriptions_license_auto_apply_data(
mock_get_default_enrollment_intentions_learner_status=mock_get_default_enrollment_intentions_learner_status,
mock_get_subscription_licenses_for_learner=mock_get_subscription_licenses_for_learner,
mock_get_enterprise_customers_for_user=mock_get_enterprise_customers_for_user,
mock_get_enterprise_course_enrollments=mock_get_enterprise_course_enrollments,
mock_get_enterprise_customer_data=mock_get_enterprise_customer_data,
mock_auto_apply_license=mock_auto_apply_license,
identity_provider=identity_provider,
is_staff_request_user=is_staff_request_user,
has_plan_for_auto_apply=has_plan_for_auto_apply,
has_existing_activated_license=has_existing_activated_license,
has_existing_revoked_license=has_existing_revoked_license,
auto_apply_with_universal_link=auto_apply_with_universal_link,
)
mock_enterprise_learner_response_data = {
**self.mock_enterprise_learner_response_data,
'results': mock_linked_enterprise_customer_users,
}
mock_get_enterprise_customers_for_user.return_value = mock_enterprise_learner_response_data
mock_auto_applied_subscription_license = {
**self.mock_subscription_license,
'status': 'activated',
'activation_date': '2024-01-01T00:00:00Z',
}
mock_subscription_licenses_data = {
**self.mock_subscription_licenses_data,
'customer_agreement': {
**self.mock_customer_agreement,
'enable_auto_applied_subscriptions_with_universal_link': auto_apply_with_universal_link,
'subscription_for_auto_applied_licenses': (
self.mock_subscription_plan_uuid
if has_plan_for_auto_apply else None
),
},
}
if has_existing_activated_license:
mock_subscription_licenses_data['results'].append({
**self.mock_subscription_license,
'status': 'activated',
'activation_date': '2024-01-01T00:00:00Z',
})
if has_existing_revoked_license:
mock_subscription_licenses_data['results'].append({
**self.mock_subscription_license,
'status': 'revoked',
})
mock_get_subscription_licenses_for_learner.return_value = mock_subscription_licenses_data
mock_auto_apply_license.return_value = mock_auto_applied_subscription_license
mock_get_default_enrollment_intentions_learner_status.return_value =\
self.mock_default_enterprise_enrollment_intentions_learner_status_data
mock_get_enterprise_course_enrollments.return_value = self.mock_enterprise_course_enrollments

query_params = {
'enterprise_customer_slug': self.mock_enterprise_customer_slug,
}
Expand Down Expand Up @@ -650,14 +715,12 @@ def test_dashboard_with_subscriptions_license_auto_apply(
expected_licenses.extend(expected_activated_licenses)
expected_licenses.extend(expected_revoked_licenses)
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': mock_enterprise_customer_with_auto_apply,
'all_linked_enterprise_customer_users': mock_linked_enterprise_customer_users,
'staff_enterprise_customer': staff_enterprise_customer,
'active_enterprise_customer': active_enterprise_customer,
'should_update_active_enterprise_customer_user': False,
'enterprise_customer_user_subsidies': {
'subscriptions': {
'customer_agreement': expected_customer_agreement,
Expand Down
23 changes: 12 additions & 11 deletions enterprise_access/apps/api_client/lms_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ class LmsApiClient(BaseOAuthClient):
"""
API client for calls to the LMS service.
"""
enterprise_api_base_url = settings.LMS_URL + '/enterprise/api/v1/'
enterprise_learner_endpoint = enterprise_api_base_url + 'enterprise-learner/'
enterprise_customer_endpoint = enterprise_api_base_url + 'enterprise-customer/'
pending_enterprise_learner_endpoint = enterprise_api_base_url + 'pending-enterprise-learner/'
enterprise_group_membership_endpoint = enterprise_api_base_url + 'enterprise-group/'
pending_enterprise_admin_endpoint = enterprise_api_base_url + 'pending-enterprise-admin/'
enterprise_base_url = settings.LMS_URL + '/enterprise/'
enterprise_api_v1_base_url = enterprise_base_url + 'api/v1/'
enterprise_learner_endpoint = enterprise_api_v1_base_url + 'enterprise-learner/'
enterprise_customer_endpoint = enterprise_api_v1_base_url + 'enterprise-customer/'
pending_enterprise_learner_endpoint = enterprise_api_v1_base_url + 'pending-enterprise-learner/'
enterprise_group_membership_endpoint = enterprise_api_v1_base_url + 'enterprise-group/'
pending_enterprise_admin_endpoint = enterprise_api_v1_base_url + 'pending-enterprise-admin/'

def enterprise_customer_url(self, enterprise_customer_uuid):
return os.path.join(
Expand All @@ -60,7 +61,7 @@ def enterprise_customer_url(self, enterprise_customer_uuid):

def enterprise_group_endpoint(self, group_uuid):
return os.path.join(
self.enterprise_api_base_url + 'enterprise-group/',
self.enterprise_api_v1_base_url + 'enterprise-group/',
f"{group_uuid}/",
)

Expand Down Expand Up @@ -553,12 +554,12 @@ class LmsUserApiClient(BaseUserApiClient):
"""
API client for user-specific calls to the LMS service.
"""
enterprise_api_base_url = f"{settings.LMS_URL}/enterprise/api/v1/"
enterprise_base_url = settings.LMS_URL + "/enterprise/"
enterprise_api_v1_base_url = enterprise_base_url + "api/v1/"
enterprise_learner_portal_api_base_url = f"{settings.LMS_URL}/enterprise_learner_portal/api/v1/"

enterprise_learner_endpoint = f"{enterprise_api_base_url}enterprise-learner/"
enterprise_learner_endpoint = f"{enterprise_api_v1_base_url}enterprise-learner/"
default_enterprise_enrollment_intentions_learner_status_endpoint = (
f'{enterprise_api_base_url}default-enterprise-enrollment-intentions/learner-status/'
f'{enterprise_api_v1_base_url}default-enterprise-enrollment-intentions/learner-status/'
)
enterprise_course_enrollments_endpoint = (
f'{enterprise_learner_portal_api_base_url}enterprise_course_enrollments/'
Expand Down
30 changes: 19 additions & 11 deletions enterprise_access/apps/bffs/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from enterprise_access.apps.api_client.license_manager_client import LicenseManagerUserApiClient
from enterprise_access.apps.api_client.lms_client import LmsApiClient, LmsUserApiClient
from enterprise_access.cache_utils import versioned_cache_key
from enterprise_access.cache_utils import request_cache, versioned_cache_key

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -41,13 +41,13 @@ def enterprise_course_enrollments_cache_key(enterprise_customer_uuid, lms_user_i
return versioned_cache_key('get_enterprise_course_enrollments', enterprise_customer_uuid, lms_user_id)


def get_and_cache_enterprise_customer_users(request, timeout=settings.ENTERPRISE_USER_RECORD_CACHE_TIMEOUT, **kwargs):
def get_and_cache_enterprise_customer_users(request, **kwargs):
"""
Retrieves and caches enterprise learner data.
"""
username = request.user.username
cache_key = enterprise_customer_users_cache_key(username)
cached_response = TieredCache.get_cached_response(cache_key)
cached_response = request_cache(namespace=REQUEST_CACHE_NAMESPACE).get_cached_response(cache_key)
if cached_response.is_found:
logger.info(
f'enterprise_customer_users cache hit for username {username}'
Expand All @@ -59,7 +59,11 @@ def get_and_cache_enterprise_customer_users(request, timeout=settings.ENTERPRISE
username=username,
**kwargs,
)
TieredCache.set_all_tiers(cache_key, response_payload, timeout)
logger.info(
'Fetched enterprise customer user for username %s',
username,
)
request_cache(namespace=REQUEST_CACHE_NAMESPACE).set(cache_key, response_payload)
return response_payload


Expand Down Expand Up @@ -258,30 +262,33 @@ def _determine_enterprise_customer_for_display(
Determine the enterprise customer user for display.
Returns:
The enterprise customer user for display.
tuple(Dict, boolean): The enterprise customer user for display, and a boolean to determine
whether to update the active enterprise customer to the return value.
"""

if not enterprise_customer_slug and not enterprise_customer_uuid:
# No enterprise customer specified in the request, so return the active enterprise customer
return active_enterprise_customer
return active_enterprise_customer, False

# If the requested enterprise does not match the active enterprise customer user's slug/uuid
# and there is a linked enterprise customer user for the requested enterprise, return the
# linked enterprise customer.
# linked enterprise customer. By returning true, we are updating the current active enterprise
# customer to the requested_enterprise_customer
request_matches_active_enterprise_customer = _request_matches_active_enterprise_customer(
active_enterprise_customer=active_enterprise_customer,
enterprise_customer_slug=enterprise_customer_slug,
enterprise_customer_uuid=enterprise_customer_uuid,
)
if not request_matches_active_enterprise_customer and requested_enterprise_customer:
return requested_enterprise_customer
return requested_enterprise_customer, True

# If the request user is staff and the requested enterprise does not match the active enterprise
# customer user's slug/uuid, return the staff-enterprise customer.
if staff_enterprise_customer:
return staff_enterprise_customer
return staff_enterprise_customer, False

# Otherwise, return the active enterprise customer.
return active_enterprise_customer
return active_enterprise_customer, False


def _request_matches_active_enterprise_customer(
Expand Down Expand Up @@ -360,7 +367,7 @@ def transform_enterprise_customer_users_data(data, request, enterprise_customer_
enterprise_customer_user_for_requested_customer.get('enterprise_customer')
if enterprise_customer_user_for_requested_customer else None
)
enterprise_customer = _determine_enterprise_customer_for_display(
enterprise_customer, should_update_active_enterprise_customer_user = _determine_enterprise_customer_for_display(
enterprise_customer_slug=enterprise_customer_slug,
enterprise_customer_uuid=enterprise_customer_uuid,
active_enterprise_customer=active_enterprise_customer,
Expand All @@ -373,4 +380,5 @@ def transform_enterprise_customer_users_data(data, request, enterprise_customer_
'active_enterprise_customer': active_enterprise_customer,
'all_linked_enterprise_customer_users': enterprise_customer_users,
'staff_enterprise_customer': staff_enterprise_customer,
'should_update_active_enterprise_customer_user': should_update_active_enterprise_customer_user,
}
Loading

0 comments on commit 20ba138

Please sign in to comment.