Skip to content

Commit

Permalink
feat: basics of business logic for customer provisioning
Browse files Browse the repository at this point in the history
  • Loading branch information
iloveagent57 committed Feb 21, 2025
1 parent b26a256 commit e17a900
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 1 deletion.
28 changes: 27 additions & 1 deletion enterprise_access/apps/api/v1/views/provisioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from enterprise_access.apps.api import serializers
from enterprise_access.apps.core import constants
from enterprise_access.apps.provisioning import api as provisioning_api

logger = logging.getLogger(__name__)

Expand All @@ -37,4 +38,29 @@ class ProvisioningCreateView(PermissionRequiredMixin, generics.CreateAPIView):
def create(self, request, *args, **kwargs):
request_serializer = serializers.ProvisioningRequestSerializer(data=request.data)
request_serializer.is_valid(raise_exception=True)
return Response('ack', status=status.HTTP_201_CREATED)

customer_request_data = request_serializer.validated_data['enterprise_customer']
created_customer = provisioning_api.get_or_create_enterprise_customer(
name=customer_request_data['name'],
country=customer_request_data['country'],
slug=customer_request_data['slug'],
)

admin_emails = [
record.get('user_email')
for record in request_serializer.validated_data['pending_admins']
]

customer_admins = provisioning_api.get_or_create_enterprise_admin_users(
enterprise_customer_uuid=created_customer['uuid'],
user_emails=admin_emails,
)

response_serializer = serializers.ProvisioningResponseSerializer({
'enterprise_customer': created_customer,
'pending_admins': customer_admins,
})
return Response(
response_serializer.data,
status=status.HTTP_201_CREATED,
)
27 changes: 27 additions & 0 deletions enterprise_access/apps/api_client/lms_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,33 @@ def get_enterprise_admin_users(self, enterprise_customer_uuid):

return results

def get_enterprise_pending_admin_users(self, enterprise_customer_uuid):
"""
Gets all pending enterprise admin records for the given customer uuid.
Arguments:
enterprise_customer_uuid (UUID): UUID of the enterprise customer.
Returns:
List of dictionaries of pending admin users.
"""
try:
response = self.client.get(
self.pending_enterprise_admin_endpoint + f'?enterprise_customer={enterprise_customer_uuid}',
timeout=settings.LMS_CLIENT_TIMEOUT,
)
response.raise_for_status()
logger.info(
'Fetched pending admin records for customer %s', enterprise_customer_uuid,
)
payload = response.json()
return payload.get('results', [])
except requests.exceptions.HTTPError:
logger.exception(
'Failed to fetch pending admin record for customer %s',
enterprise_customer_uuid,
)
raise

def create_enterprise_admin_user(self, enterprise_customer_uuid, user_email):
"""
Creates a new enterprise pending admin record.
Expand Down
62 changes: 62 additions & 0 deletions enterprise_access/apps/api_client/tests/test_lms_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,43 @@ def test_create_enterprise_customer_data(self, mock_oauth_client, mock_json):
timeout=settings.LMS_CLIENT_TIMEOUT,
)

@mock.patch('requests.Response.json')
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
def test_get_enterprise_pending_admin_users(self, mock_oauth_client, mock_json):
"""
Test that we can use the LmsApiClient to fetch existing pending admin records.
"""
customer_uuid = str(uuid4())

mock_response_payload_results = [{
'id': 1,
'enterprise_customer': customer_uuid,
'user_email': '[email protected]',
}]
mock_response_payload = {
'count': 1,
'results': mock_response_payload_results,
}
mock_json.return_value = mock_response_payload

mock_get = mock_oauth_client.return_value.get

mock_get.return_value = requests.Response()
mock_get.return_value.status_code = 200

client = LmsApiClient()
response_payload = client.get_enterprise_pending_admin_users(customer_uuid)

self.assertEqual(response_payload, mock_response_payload_results)
expected_url = (
'http://edx-platform.example.com/enterprise/api/v1/pending-enterprise-admin/'
f'?enterprise_customer={customer_uuid}'
)
mock_get.assert_called_once_with(
expected_url,
timeout=settings.LMS_CLIENT_TIMEOUT,
)

@mock.patch('requests.Response.json')
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
def test_create_enterprise_admin_user(self, mock_oauth_client, mock_json):
Expand Down Expand Up @@ -316,6 +353,31 @@ def test_create_enterprise_admin_error(self, mock_oauth_client):
timeout=settings.LMS_CLIENT_TIMEOUT,
)

@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
def test_get_enterprise_pending_admin_error(self, mock_oauth_client):
"""
Tests that we raise an exception appropriately when listing pending
admin records with the LmsApiClient().
"""
customer_uuid = str(uuid4())
mock_get = mock_oauth_client.return_value.get

mock_get.side_effect = requests.exceptions.HTTPError('whoopsie')
mock_get.return_value.status_code = 400

client = LmsApiClient()
with self.assertRaises(requests.exceptions.HTTPError):
client.get_enterprise_pending_admin_users(customer_uuid)

expected_url = (
'http://edx-platform.example.com/enterprise/api/v1/pending-enterprise-admin/'
f'?enterprise_customer={customer_uuid}'
)
mock_get.assert_called_once_with(
expected_url,
timeout=settings.LMS_CLIENT_TIMEOUT,
)

@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
def test_unlink_users_from_enterprise(self, mock_oauth_client):
"""
Expand Down
Empty file.
64 changes: 64 additions & 0 deletions enterprise_access/apps/provisioning/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Python API for provisioning operations.
"""
import logging

from ..api_client.lms_client import LmsApiClient

logger = logging.getLogger(__name__)


def get_or_create_enterprise_customer(*, name, slug, country, **kwargs):
"""
Get or creates an enterprise customer with the provided arguments.
"""
client = LmsApiClient()
existing_customer = client.get_enterprise_customer_data(enterprise_customer_slug=slug)
if existing_customer:
logger.info('Provisioning: enterprise_customer slug %s already exists', slug)
return existing_customer

created_customer = client.create_enterprise_customer(
name=name, slug=slug, country=country, **kwargs,
)
logger.info('Provisioning: created enterprise customer with slug %s', slug)


def get_or_create_enterprise_admin_users(enterprise_customer_uuid, user_emails):
"""
Creates pending admin records from the given ``user_email`` for the customer
identified by ``enterprise_customer_uuid``.
"""
client = LmsApiClient()
existing_admins = client.get_enterprise_admin_users(enterprise_customer_uuid)
existing_admin_emails = {record['email'] for record in existing_admins}
logger.info(
'Provisioning: customer %s has existing admin emails %s',
enterprise_customer_uuid,
existing_admin_emails,
)

existing_pending_admins = client.get_enterprise_pending_admin_users(enterprise_customer_uuid)
existing_pending_admin_emails = {record['user_email'] for record in existing_pending_admins}
logger.info(
'Provisioning: customer %s has existing pending admin emails %s',
enterprise_customer_uuid,
existing_pending_admin_emails,
)

user_emails_to_create = list(
(set(user_emails) - existing_admin_emails) - existing_pending_admin_emails
)

created_admins = []
for user_email in user_emails_to_create:
created_admins.append(
client.create_enterprise_admin_user(enterprise_customer_uuid, user_email)
)
logger.info(
'Provisioning: created admin %s for customer %s',
user_email,
enterprise_customer_uuid,
)

return created_admins + existing_pending_admins

0 comments on commit e17a900

Please sign in to comment.