Skip to content
Snippets Groups Projects
Commit ac43d5fa authored by Dillon Dumesnil's avatar Dillon Dumesnil
Browse files

Adding email opt in to create entitlement

parent 0fe187df
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