Skip to content
Snippets Groups Projects
Unverified Commit fbb5e6c9 authored by Dillon-Dumesnil's avatar Dillon-Dumesnil Committed by GitHub
Browse files

Merge pull request #19139 from edx/ddumesnil/entitlements-email-opt-in

Adding email opt in to create entitlement
parents 1cda2411 ac43d5fa
Branches
Tags
No related merge requests found
......@@ -20,6 +20,7 @@ from course_modes.tests.factories import CourseModeFactory
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.models import UserOrgTag
from student.models import CourseEnrollment
from student.tests.factories import (TEST_PASSWORD, CourseEnrollmentFactory, UserFactory)
......@@ -309,6 +310,47 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
)
assert course_entitlement.policy == policy
@patch("entitlements.api.v1.views.get_owners_for_course")
def test_email_opt_in_single_org(self, mock_get_owners):
course_uuid = uuid.uuid4()
entitlement_data = self._get_data_set(self.user, str(course_uuid))
entitlement_data['email_opt_in'] = True
org = u'particularly'
mock_get_owners.return_value = [{'key': org}]
response = self.client.post(
self.entitlements_list_url,
data=json.dumps(entitlement_data),
content_type='application/json',
)
assert response.status_code == 201
result_obj = UserOrgTag.objects.get(user=self.user, org=org, key='email-optin')
self.assertEqual(result_obj.value, u"True")
@patch("entitlements.api.v1.views.get_owners_for_course")
def test_email_opt_in_multiple_orgs(self, mock_get_owners):
course_uuid = uuid.uuid4()
entitlement_data = self._get_data_set(self.user, str(course_uuid))
entitlement_data['email_opt_in'] = True
org_1 = u'particularly'
org_2 = u'underwood'
mock_get_owners.return_value = [{'key': org_1}, {'key': org_2}]
response = self.client.post(
self.entitlements_list_url,
data=json.dumps(entitlement_data),
content_type='application/json',
)
assert response.status_code == 201
result_obj = UserOrgTag.objects.get(user=self.user, org=org_1, key='email-optin')
self.assertEqual(result_obj.value, u"True")
result_obj = UserOrgTag.objects.get(user=self.user, org=org_2, key='email-optin')
self.assertEqual(result_obj.value, u"True")
def test_add_entitlement_with_support_detail(self):
"""
Verify that an EntitlementSupportDetail entry is made when the request includes support interaction information.
......
......@@ -19,9 +19,10 @@ from entitlements.api.v1.permissions import IsAdminOrSupportOrAuthenticatedReadO
from entitlements.api.v1.serializers import CourseEntitlementSerializer
from entitlements.models import CourseEntitlement, CourseEntitlementPolicy, CourseEntitlementSupportDetail
from entitlements.utils import is_course_run_entitlement_fulfillable
from openedx.core.djangoapps.catalog.utils import get_course_runs_for_course
from openedx.core.djangoapps.catalog.utils import get_course_runs_for_course, get_owners_for_course
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
from student.models import AlreadyEnrolledError, CourseEnrollment, CourseEnrollmentException
log = logging.getLogger(__name__)
......@@ -158,6 +159,8 @@ class EntitlementViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
support_details = request.data.pop('support_details', [])
email_opt_in = request.data.pop('email_opt_in', False)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
......@@ -165,6 +168,12 @@ class EntitlementViewSet(viewsets.ModelViewSet):
entitlement = serializer.instance
set_entitlement_policy(entitlement, request.site)
# The owners for a course are the organizations that own the course. By taking owner.key,
# we are able to pass in the organization key for email_opt_in
owners = get_owners_for_course(entitlement.course_uuid)
for owner in owners:
update_email_opt_in(entitlement.user, owner['key'], email_opt_in)
if support_details:
for support_detail in support_details:
support_detail['entitlement'] = entitlement
......
......@@ -35,9 +35,10 @@ from openedx.core.djangoapps.catalog.utils import (
get_course_runs,
get_course_runs_for_course,
get_course_run_details,
get_pathways,
get_currency_data,
get_localized_price_text,
get_owners_for_course,
get_pathways,
get_program_types,
get_programs,
get_visible_sessions_for_entitlement
......@@ -464,6 +465,31 @@ class TestGetCourseRuns(CatalogIntegrationMixin, TestCase):
self.assertEqual(data, catalog_course_runs)
@skip_unless_lms
@mock.patch(UTILS_MODULE + '.get_edx_api_data')
class TestGetCourseOwners(CatalogIntegrationMixin, TestCase):
"""
Tests covering retrieval of course runs from the catalog service.
"""
def setUp(self):
super(TestGetCourseOwners, self).setUp()
self.catalog_integration = self.create_catalog_integration(cache_ttl=1)
self.user = UserFactory(username=self.catalog_integration.service_username)
def test_get_course_owners_by_course(self, mock_get_edx_api_data):
"""
Test retrieval of course runs.
"""
catalog_course_runs = CourseRunFactory.create_batch(10)
catalog_course = CourseFactory(course_runs=catalog_course_runs)
mock_get_edx_api_data.return_value = catalog_course
data = get_owners_for_course(course_uuid=str(catalog_course['uuid']))
self.assertTrue(mock_get_edx_api_data.called)
self.assertEqual(data, catalog_course['owners'])
@skip_unless_lms
@mock.patch(UTILS_MODULE + '.get_edx_api_data')
class TestSessionEntitlement(CatalogIntegrationMixin, TestCase):
......
......@@ -37,6 +37,45 @@ def create_catalog_api_client(user, site=None):
return EdxRestApiClient(url, jwt=jwt)
def check_catalog_integration_and_get_user(error_message_field):
"""
Checks that catalog integration is enabled, and if so, attempts to get and
return the service user.
Parameters:
error_message_field (str): The field that will be attempted to be
retrieved when calling the api client. Used for the error message.
Returns:
Tuple of:
The catalog integration service user if it exists, else None
The catalog integration Object
Note: (This is necessary for future calls of functions using this method)
"""
catalog_integration = CatalogIntegration.current()
if catalog_integration.is_enabled():
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
logger.error(
'Catalog service user with username [{username}] does not exist. '
'{field} will not be retrieved.'.format(
username=catalog_integration.service_username,
field=error_message_field,
)
)
return None, catalog_integration
return user, catalog_integration
else:
logger.error(
'Unable to retrieve details about {field} because Catalog Integration is not enabled'.format(
field=error_message_field,
)
)
return None, catalog_integration
def get_programs(site, uuid=None):
"""Read programs from the cache.
......@@ -101,13 +140,8 @@ def get_program_types(name=None):
list of dict, representing program types.
dict, if a specific program type is requested.
"""
catalog_integration = CatalogIntegration.current()
if catalog_integration.enabled:
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
return []
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Program types')
if user:
api = create_catalog_api_client(user)
cache_key = '{base}.program_types'.format(base=catalog_integration.CACHE_KEY)
......@@ -186,13 +220,8 @@ def get_currency_data():
list of dict, representing program types.
dict, if a specific program type is requested.
"""
catalog_integration = CatalogIntegration.current()
if catalog_integration.enabled:
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
return []
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Currency data')
if user:
api = create_catalog_api_client(user)
cache_key = '{base}.currency'.format(base=catalog_integration.CACHE_KEY)
......@@ -289,18 +318,9 @@ def get_course_runs():
Returns:
list of dict with each record representing a course run.
"""
catalog_integration = CatalogIntegration.current()
course_runs = []
if catalog_integration.enabled:
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
logger.error(
'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
catalog_integration.service_username,
)
return course_runs
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Course runs')
if user:
api = create_catalog_api_client(user)
querystring = {
......@@ -314,18 +334,30 @@ def get_course_runs():
def get_course_runs_for_course(course_uuid):
catalog_integration = CatalogIntegration.current()
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Course runs')
if user:
api = create_catalog_api_client(user)
cache_key = '{base}.course.{uuid}.course_runs'.format(
base=catalog_integration.CACHE_KEY,
uuid=course_uuid
)
data = get_edx_api_data(
catalog_integration,
'courses',
resource_id=course_uuid,
api=api,
cache_key=cache_key if catalog_integration.is_cache_enabled else None,
long_term_cache=True,
many=False
)
return data.get('course_runs', [])
else:
return []
if catalog_integration.is_enabled():
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
logger.error(
'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
catalog_integration.service_username,
)
return []
def get_owners_for_course(course_uuid):
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Owners')
if user:
api = create_catalog_api_client(user)
cache_key = '{base}.course.{uuid}.course_runs'.format(
base=catalog_integration.CACHE_KEY,
......@@ -340,7 +372,7 @@ def get_course_runs_for_course(course_uuid):
long_term_cache=True,
many=False
)
return data.get('course_runs', [])
return data.get('owners', [])
else:
return []
......@@ -356,18 +388,8 @@ def get_course_uuid_for_course(course_run_key):
Returns:
UUID: Course UUID and None if it was not retrieved.
"""
catalog_integration = CatalogIntegration.current()
if catalog_integration.is_enabled():
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
logger.error(
'Catalog service user with username [%s] does not exist. Course UUID will not be retrieved.',
catalog_integration.service_username,
)
return []
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Course UUID')
if user:
api = create_catalog_api_client(user)
run_cache_key = '{base}.course_run.{course_run_key}'.format(
......@@ -483,27 +505,15 @@ def get_course_run_details(course_run_key, fields):
Returns:
dict with language, start date, end date, and max_effort details about specified course run
"""
catalog_integration = CatalogIntegration.current()
course_run_details = dict()
if catalog_integration.enabled:
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
msg = 'Catalog service user {} does not exist. Data for course_run {} will not be retrieved'.format(
catalog_integration.service_username,
course_run_key
)
logger.error(msg)
return course_run_details
user, catalog_integration = check_catalog_integration_and_get_user(
error_message_field='Data for course_run {}'.format(course_run_key)
)
if user:
api = create_catalog_api_client(user)
cache_key = '{base}.course_runs'.format(base=catalog_integration.CACHE_KEY)
course_run_details = get_edx_api_data(catalog_integration, 'course_runs', api, resource_id=course_run_key,
cache_key=cache_key, many=False, traverse_pagination=False, fields=fields)
else:
msg = 'Unable to retrieve details about course_run {} because Catalog Integration is not enabled'.format(
course_run_key
)
logger.error(msg)
return course_run_details
......@@ -332,9 +332,9 @@ class UpdateEmailOptInTests(ModuleStoreTestCase):
"""
Test cases to cover API-driven email list opt-in update workflows
"""
USERNAME = u'frank-underwood'
USERNAME = u'claire-underwood'
PASSWORD = u'ṕáśśẃőŕd'
EMAIL = u'frank+underwood@example.com'
EMAIL = u'claire+underwood@example.com'
shard = 2
@ddt.data(
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment