Skip to content

Commit

Permalink
fix: create CEA object when enrolling using a license flow
Browse files Browse the repository at this point in the history
  • Loading branch information
0x29a committed Apr 22, 2024
1 parent ad03ac6 commit 2381ecf
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 18 deletions.
30 changes: 30 additions & 0 deletions enterprise/api_client/lms.py
Expand Up @@ -6,6 +6,8 @@
import time
from urllib.parse import urljoin

import requests

from opaque_keys.edx.keys import CourseKey
from requests.exceptions import ( # pylint: disable=redefined-builtin
ConnectionError,
Expand Down Expand Up @@ -274,6 +276,34 @@ def get_enrolled_courses(self, username):
response.raise_for_status()
return response.json()

def allow_enrollment(self, email, course_id, auto_enroll=False):
"""
Call the enrollment API to allow enrollment for the given email and course_id.
Args:
email (str): The email address of the user to be allowed to enroll in the course.
course_id (str): The string value of the course's unique identifier.
auto_enroll (bool): Whether to auto-enroll the user in the course upon registration / activation.
Returns:
dict: A dictionary containing details of the created CourseEnrollmentAllowed object.
"""
api_url = self.get_api_url("enrollment_allowed")
response = self.client.post(
f"{api_url}/",
json={
'email': email,
'course_id': course_id,
'auto_enroll': auto_enroll,
}
)
if response.status_code == requests.codes.conflict:
LOGGER.info(response.json()["message"])
else:
response.raise_for_status()
return response.json()


class CourseApiClient(NoAuthAPIClient):
"""
Expand Down
11 changes: 3 additions & 8 deletions enterprise/utils.py
Expand Up @@ -2407,19 +2407,14 @@ def truncate_string(string, max_length=MAX_ALLOWED_TEXT_LENGTH):

def ensure_course_enrollment_is_allowed(course_id, email, enrollment_api_client):
"""
Create a CourseEnrollmentAllowed object for invitation-only courses.
Calls the enrollment API to create a CourseEnrollmentAllowed object for
invitation-only courses.
Arguments:
course_id (str): ID of the course to allow enrollment
email (str): email of the user whose enrollment should be allowed
enrollment_api_client (:class:`enterprise.api_client.lms.EnrollmentApiClient`): Enrollment API Client
"""
if not CourseEnrollmentAllowed:
raise NotConnectedToOpenEdX()

course_details = enrollment_api_client.get_course_details(course_id)
if course_details["invite_only"]:
CourseEnrollmentAllowed.objects.update_or_create(
course_id=course_id,
email=email,
)
enrollment_api_client.allow_enrollment(email, course_id)
9 changes: 9 additions & 0 deletions enterprise/views.py
Expand Up @@ -683,6 +683,15 @@ def _enroll_learner_in_course(
existing_enrollment.get('mode') == constants.CourseModes.AUDIT or
existing_enrollment.get('is_active') is False
):
if enterprise_customer.allow_enrollment_in_invite_only_courses:
ensure_course_enrollment_is_allowed(course_id, request.user.email, enrollment_api_client)
LOGGER.info(
'User {user} is allowed to enroll in Course {course_id}.'.format(
user=request.user.username,
course_id=course_id
)
)

course_mode = get_best_mode_from_course_key(course_id)
LOGGER.info(
'Retrieved Course Mode: {course_modes} for Course {course_id}'.format(
Expand Down
11 changes: 5 additions & 6 deletions tests/test_enterprise/test_utils.py
Expand Up @@ -539,10 +539,9 @@ def test_truncate_string(self):
self.assertEqual(len(truncated_string), MAX_ALLOWED_TEXT_LENGTH)

@ddt.data(True, False)
@mock.patch("enterprise.utils.CourseEnrollmentAllowed")
def test_ensure_course_enrollment_is_allowed(self, invite_only, mock_cea):
def test_ensure_course_enrollment_is_allowed(self, invite_only):
"""
Test that the CourseEnrollmentAllowed is created only for the "invite_only" courses.
Test that the enrollment allow endpoint is called for the "invite_only" courses.
"""
self.create_user()
mock_enrollment_api = mock.Mock()
Expand All @@ -551,9 +550,9 @@ def test_ensure_course_enrollment_is_allowed(self, invite_only, mock_cea):
ensure_course_enrollment_is_allowed("test-course-id", self.user.email, mock_enrollment_api)

if invite_only:
mock_cea.objects.update_or_create.assert_called_with(
mock_enrollment_api.return_value.allow_enrollment.assert_called_with(
email=self.user.email,
course_id="test-course-id",
email=self.user.email
)
else:
mock_cea.objects.update_or_create.assert_not_called()
mock_enrollment_api.return_value.allow_enrollment.assert_not_called()
6 changes: 2 additions & 4 deletions tests/test_enterprise/views/test_course_enrollment_view.py
Expand Up @@ -1623,10 +1623,8 @@ def test_post_course_specific_enrollment_view_premium_mode(
@mock.patch('enterprise.views.EnrollmentApiClient')
@mock.patch('enterprise.views.get_data_sharing_consent')
@mock.patch('enterprise.utils.Registry')
@mock.patch('enterprise.utils.CourseEnrollmentAllowed')
def test_post_course_specific_enrollment_view_invite_only_courses(
self,
mock_cea,
registry_mock,
get_data_sharing_consent_mock,
enrollment_api_client_mock,
Expand Down Expand Up @@ -1664,9 +1662,9 @@ def test_post_course_specific_enrollment_view_invite_only_courses(
}
)

mock_cea.objects.update_or_create.assert_called_with(
enrollment_api_client_mock.return_value.allow_enrollment.assert_called_with(
email=self.user.email,
course_id=course_id,
email=self.user.email
)
assert response.status_code == 302

Expand Down

0 comments on commit 2381ecf

Please sign in to comment.